@@ -12,6 +12,23 @@ namespace Devlooped.Html;
1212/// </summary>
1313public static class HtmlDocument
1414{
15+ const string DefaultPublicIdentifier = "-//W3C//DTD XHTML 1.0 Transitional//EN" ;
16+ const string DefaultSystemLiteral = "http://www.w3.org/TR/html4/loose.dtd" ;
17+
18+ /// <summary>
19+ /// Create a new <see cref="XDocument"/> and initialize its underlying XML tree using
20+ /// the passed <see cref="Stream"/> parameter.
21+ /// </summary>
22+ /// <param name="stream">
23+ /// A <see cref="Stream"/> containing the raw HTML to read into the newly
24+ /// created <see cref="XDocument"/>.
25+ /// </param>
26+ /// <returns>
27+ /// A new <see cref="XDocument"/> containing the contents of the passed in
28+ /// <see cref="Stream"/>.
29+ /// </returns>
30+ public static XDocument Load ( Stream stream ) => Load ( stream , HtmlReaderSettings . Default ) ;
31+
1532 /// <overloads>
1633 /// The Load method provides multiple strategies for creating a new
1734 /// <see cref="XDocument"/> and initializing it from a data source containing
@@ -35,11 +52,22 @@ public static class HtmlDocument
3552 /// An <see cref="XDocument"/> initialized with the contents of the file referenced
3653 /// in the passed in uri parameter.
3754 /// </returns>
38- public static XDocument Load ( string uri )
39- {
40- using var stream = ( Stream ) new XmlUrlResolver ( ) . GetEntity ( new Uri ( uri ) , string . Empty , typeof ( Stream ) ) ;
41- return Load ( stream ) ;
42- }
55+ public static XDocument Load ( string uri ) => Load ( uri , HtmlReaderSettings . Default ) ;
56+
57+ /// <summary>
58+ /// Create a new <see cref="XDocument"/> and initialize its underlying XML tree using
59+ /// the passed <see cref="TextReader"/> parameter. Optionally whitespace handling
60+ /// can be preserved.
61+ /// </summary>
62+ /// <param name="textReader">
63+ /// A <see cref="TextReader"/> containing the raw HTML to read into the newly
64+ /// created <see cref="XDocument"/>.
65+ /// </param>
66+ /// <returns>
67+ /// A new <see cref="XDocument"/> containing the contents of the passed in
68+ /// <see cref="TextReader"/>.
69+ /// </returns>
70+ public static XDocument Load ( TextReader textReader ) => Load ( textReader , HtmlReaderSettings . Default ) ;
4371
4472 /// <summary>
4573 /// Create a new <see cref="XDocument"/> and initialize its underlying XML tree using
@@ -49,12 +77,13 @@ public static XDocument Load(string uri)
4977 /// A <see cref="Stream"/> containing the raw HTML to read into the newly
5078 /// created <see cref="XDocument"/>.
5179 /// </param>
80+ /// <param name="settings">The settings for the HTML load process.</param>
5281 /// <returns>
5382 /// A new <see cref="XDocument"/> containing the contents of the passed in
5483 /// <see cref="Stream"/>.
5584 /// </returns>
56- public static XDocument Load ( Stream stream )
57- => Load ( new StreamReader ( stream ) ) ;
85+ public static XDocument Load ( Stream stream , HtmlReaderSettings settings )
86+ => Load ( new StreamReader ( stream ) , settings ) ;
5887
5988 /// <overloads>
6089 /// The Load method provides multiple strategies for creating a new
@@ -81,48 +110,13 @@ public static XDocument Load(Stream stream)
81110 /// in the passed in uri parameter.
82111 /// </returns>
83112 public static XDocument Load ( string uri , HtmlReaderSettings settings )
84- {
85- using var stream = ( Stream ) new XmlUrlResolver ( ) . GetEntity ( new Uri ( uri ) , string . Empty , typeof ( Stream ) ) ;
86- return Load ( stream , settings ) ;
87- }
88-
89- /// <summary>
90- /// Create a new <see cref="XDocument"/> and initialize its underlying XML tree using
91- /// the passed <see cref="Stream"/> parameter.
92- /// </summary>
93- /// <param name="stream">
94- /// A <see cref="Stream"/> containing the raw HTML to read into the newly
95- /// created <see cref="XDocument"/>.
96- /// </param>
97- /// <param name="settings">The settings for the HTML load process.</param>
98- /// <returns>
99- /// A new <see cref="XDocument"/> containing the contents of the passed in
100- /// <see cref="Stream"/>.
101- /// </returns>
102- public static XDocument Load ( Stream stream , HtmlReaderSettings settings )
103- => Load ( new StreamReader ( stream ) , settings ) ;
104-
105- /// <summary>
106- /// Create a new <see cref="XDocument"/> and initialize its underlying XML tree using
107- /// the passed <see cref="TextReader"/> parameter. Optionally whitespace handling
108- /// can be preserved.
109- /// </summary>
110- /// <param name="textReader">
111- /// A <see cref="TextReader"/> containing the raw HTML to read into the newly
112- /// created <see cref="XDocument"/>.
113- /// </param>
114- /// <returns>
115- /// A new <see cref="XDocument"/> containing the contents of the passed in
116- /// <see cref="TextReader"/>.
117- /// </returns>
118- public static XDocument Load ( TextReader textReader )
119113 {
120114 using var reader = new SgmlReader
121115 {
122- InputStream = textReader ,
116+ Href = uri ,
123117 } ;
124118
125- return XDocument . Load ( new IgnoreXmlNsReader ( reader ) ) ;
119+ return XDocument . Load ( Configure ( reader , settings ) ) ;
126120 }
127121
128122 /// <summary>
@@ -144,12 +138,9 @@ public static XDocument Load(TextReader textReader, HtmlReaderSettings settings)
144138 using var reader = new SgmlReader
145139 {
146140 InputStream = textReader ,
147- CaseFolding = settings . CaseFolding ,
148- WhitespaceHandling = settings . WhitespaceHandling ,
149- TextWhitespace = settings . TextWhitespace ,
150141 } ;
151142
152- return XDocument . Load ( settings . IgnoreXmlNamespaces ? new IgnoreXmlNsReader ( reader ) : reader ) ;
143+ return XDocument . Load ( Configure ( reader , settings ) ) ;
153144 }
154145
155146 /// <overloads>
@@ -165,8 +156,7 @@ public static XDocument Load(TextReader textReader, HtmlReaderSettings settings)
165156 /// An <see cref="XDocument"/> containing an XML tree initialized from the
166157 /// passed in HTML string.
167158 /// </returns>
168- public static XDocument Parse ( string html )
169- => Load ( new StringReader ( html ) ) ;
159+ public static XDocument Parse ( string html ) => Parse ( html , HtmlReaderSettings . Default ) ;
170160
171161 /// <overloads>
172162 /// Create a new <see cref="XDocument"/> from a string containing
@@ -187,6 +177,27 @@ public static XDocument Parse(string html)
187177 public static XDocument Parse ( string html , HtmlReaderSettings settings )
188178 => Load ( new StringReader ( html ) , settings ) ;
189179
180+ static XmlReader Configure ( SgmlReader reader , HtmlReaderSettings settings )
181+ {
182+ reader . DocType = "html" ;
183+ reader . PublicIdentifier = DefaultPublicIdentifier ;
184+ reader . SystemLiteral = DefaultSystemLiteral ;
185+
186+ reader . CaseFolding = settings . CaseFolding ;
187+ reader . WhitespaceHandling = settings . WhitespaceHandling ;
188+ reader . TextWhitespace = settings . TextWhitespace ;
189+
190+ XmlReader result = reader ;
191+
192+ if ( settings . IgnoreXmlNamespaces )
193+ result = new IgnoreXmlNsReader ( result ) ;
194+
195+ if ( settings . SkipElements . Length > 0 )
196+ result = new SkipElementsReader ( result , settings . SkipElements ) ;
197+
198+ return result ;
199+ }
200+
190201 /// <summary>
191202 /// Removes all XML namespaces, since for HTML content it's typically
192203 /// irrelevant.
@@ -242,5 +253,28 @@ public override bool MoveToNextAttribute()
242253
243254 bool IsLocalXmlNs => Prefix == "xmlns" ;
244255 }
256+
257+ /// <summary>
258+ /// Removes all XML namespaces, since for HTML content it's typically
259+ /// irrelevant.
260+ /// </summary>
261+ class SkipElementsReader : XmlWrappingReader
262+ {
263+ readonly HashSet < string > skipElements ;
264+
265+ public SkipElementsReader ( XmlReader baseReader , string [ ] skipElements ) : base ( baseReader )
266+ {
267+ this . skipElements = new HashSet < string > ( skipElements , StringComparer . OrdinalIgnoreCase ) ;
268+ }
269+
270+ public override bool Read ( )
271+ {
272+ var read = base . Read ( ) ;
273+ if ( read && base . NodeType == XmlNodeType . Element && skipElements . Contains ( LocalName ) )
274+ base . Skip ( ) ;
275+
276+ return read ;
277+ }
278+ }
245279}
246280
0 commit comments