If you are moving HTML parsing from AngleSharp to onehtml, this is what changes in the trees you get. On a corpus of real pages the two agree exactly on about nine in ten pages, and every difference in the rest comes from one of a short list of documented causes.
onehtml’s query members follow the DOM standard: QuerySelector, QuerySelectorAll, Matches, Closest, GetAttribute, TextContent, and Children for element children. Code written against those DOM names maps directly. The onehtml repository compares the two parsers on 13,857 crawled pages. 12,469 (89.98%) give identical trees, and every difference in the rest is traced to the HTML, Encoding or DOM standard in AngleSharp differential.
This program runs the documented inputs through onehtml:
using System.Text; using RecursiveAI.OneHtml; void Show(string label, string html, string selector) { Document doc = Document.Parse(html); Node node = doc.Root.QuerySelector(selector); Console.WriteLine($"{label,-26} {doc.SerializeNode(node)}"); } // SVG keeps the xlink prefix on adjusted foreign attributes Document svg = Document.Parse("<svg><use xlink:href=\"#icon\"></use></svg>"); foreach (Attr attr in svg.Root.QuerySelector("use").Attributes) { Console.WriteLine($"{"xlink attribute",-26} qualified {attr.QualifiedName}, local {attr.LocalName}, namespace {attr.Namespace}"); } // A bare & before ; is text, and the ; stays Show("bare ampersand", "<p>a&;b</p>", "p"); // select content is parsed as in body: other elements stay inside the select Show("select content", "<select><option>One<b>!</b></option></select>", "select"); // A parsed document keeps declarative shadow root templates as template elements Show("shadowrootmode", "<div><template shadowrootmode=open><b>x</b></template></div>", "div"); // search closes an open p and its end tag pops to it Show("search element", "<div><search><div></search>x</div>", "body"); // </td> does not close a th Show("cell end tag", "<table><tr><th></td>x</table>", "tr"); // Encodings: gb2312 is GBK, and undeclared pages fall back to windows-1252 Console.WriteLine($"{"gb2312 label",-26} {Document.Parse(Encoding.ASCII.GetBytes("<meta charset=gb2312><p>x")).Source.Encoding.Name}"); Console.WriteLine($"{"no declaration",-26} {Document.Parse(Encoding.ASCII.GetBytes("<p>x")).Source.Encoding.Name}");
xlink attribute qualified xlink:href, local href, namespace XLink bare ampersand <p>a&;b</p> select content <select><option>One<b>!</b></option></select> shadowrootmode <div><template shadowrootmode="open"><b>x</b></template></div> search element <body><div><search><div></div></search>x</div></body> cell end tag <tr><th>x</th></tr> gb2312 label GBK no declaration windows-1252
The same inputs in AngleSharp, as that document records them:
| Input | onehtml | AngleSharp |
|---|---|---|
<use xlink:href> in SVG | prefix xlink, local name href in the XLink namespace | plain href (namespace kept, prefix dropped). With both href and xlink:href, keeps one |
a&;b | text a&;b | drops the ; |
elements inside <select> | kept, as the current standard parses select content as in body | dropped or moved (the older “in select” mode) |
<template shadowrootmode> in a parsed document | stays a template element with its content | attaches a shadow root, so the host has no child |
<search> | closes an open p and its end tag pops to it | unknown element, so </search> is ignored |
</td> while a th is open | ignored | closes the cell |
gb2312 / gbk labels | GBK | gb18030 (the decoded text is the same) |
no BOM, header, <meta> or XML declaration | windows-1252, the standard’s recommended default | UTF-8 |
<?xml … encoding="Shift_JIS"?> with no other declaration | Shift_JIS | UTF-8 |
uppercase </C at the end of a <style> | text </C | text </c |
| thousands of unclosed elements | nests up to 4,096, then throws HtmlLimitExceededException | stops nesting at 512 and appends siblings |
Other differences to plan for:
- Undeclared pages decode differently. This is the change most likely to show up in your output. Where AngleSharp assumed UTF-8, onehtml uses windows-1252. If your sources are UTF-8 without a declaration, set
ParseOptions.DefaultEncoding = WhatwgEncoding.Utf8(see Detect the encoding). - Nothing is fetched and no script runs. onehtml never opens a URL or a file. Fetch the bytes yourself, pass the
Content-Typeheader asParseOptions.ContentType, and supply linked stylesheets through a resolver (see Compute CSS styles). - Documents are immutable. You don’t change a node in place. You build edits and
Applythem, which returns a new snapshot and leaves every untouched byte of the original as it was (see Edit HTML without reformatting it). Nodeis a struct. A missing result is the null node (IsNull), notnull.- The target framework is
net10.0only.
For a measured comparison of the trees each parser builds: HtmlAgilityPack vs AngleSharp. Coming from HtmlAgilityPack instead: Migrate from HtmlAgilityPack.