将XHTML5解析为XDocument

4
我需要将XHTML5文件解析为XDocument实例。我的文件将始终是格式良好的XML,因此我想避免使用HtmlAgilityPack,因为它对格式不正确的XHTML过于宽容。 XDocument.Load方法适用于简单情况,但在文档中包含命名字符引用(实体)时会出现错误:
var xhtml = XDocument.Load(reader);
// XmlException: Reference to undeclared entity 'nbsp'. 

对于XHTML 1.0,可以使用XmlPreloadedResolver来解决这个问题,该方法预加载了在XHTML 1.0中定义的众所周知的DTD。可以通过手动提供其DTD的方式扩展此方法以支持XHTML 1.1,如此答案所示。
然而,XHTML5没有DTD,如在另一个答案中讨论的那样。它的实体定义仅供信息参考,以JSON格式提供。
<!DOCTYPE html>

因此,在解析XHTML5实体时,XmlResolver方法不会被调用。有关尝试为提供XmlReader实体声明列表的讨论,但似乎没有一种方法可以直接奏效。
目前,我正在研究两种方法。第一种是在文档类型声明中指定包含实体声明的内部子集,可以通过对源XHTML进行字符串操作或者使用XmlParserContext.InternalSubset来实现。这将导致类似于以下的文档类型声明:
<!DOCTYPE html [
  <!ENTITY ndash "&#8211;">
  <!ENTITY nbsp "&#160;">
  ...
]>

似乎在XHTML5中允许这样做;但是,这是不可取的,因为它会在XDocument中留下实体声明(现在有2000多个),如果用户将其转换回字符串表示形式,则可能会出现问题。
我的另一种方法是使用正则表达式预处理XHTML字符串,将所有命名字符引用转换为数字字符引用(或实际Unicode字符),但排除XML预定义实体" & ' < >。然而,我担心在XML的定义中存在复杂性,这种方法可能会错过。例如,这个答案表明,字符不能在注释、CDATA部分或处理指令中转义。我认为我的正则表达式需要进行调整,以排除所有这些情况。
是否有任何经验或建议关于这两种方法,或者您考虑的其他方法?我更喜欢基于XmlReader的可扩展性的方法,但如果没有其他方法,我将采用源字符串操作。

最后一条评论似乎更新了有关官方XHTML5字符实体列表(作为DTD)的信息。我以前没有做过这个,但如果我正确理解了您的解释,那么在给定DTD的情况下,现在可以使用“XmlPreloadedResolver”方法... - har07
我没有尝试过,但是使用包含DTD的XHTML1.1文档类型声明,然后使用诸如“<!ELEMENT section (#PCDATA | %Flow.mix;)*>”之类的标记声明定义新的HTML5元素,这个方法怎么样? - Mr Lister
这可能听起来像手动定义所有新实体一样笨拙,但与XHTML1.1相比,在HTML5中有远少于2000个新元素! - Mr Lister
我可以稍后测试,但也许可以使用实体映射的doctype,然后首先使用身份转换进行转换。我相信这将为您执行替换,就像尝试编写一些巨大的正则表达式一样。 - Kevin Brown
1个回答

1
如果您在应用实体映射的情况下对源文档使用“identity translate”,它会在结果中为您替换实际字符。对我来说,这与正则表达式没有什么不同(只是一步),而且肯定更简单。
鉴于此源代码:
<!DOCTYPE foo [
 <!ENTITY ndash "&#8211;">
 <!ENTITY nbsp "&#160;">
]>
<foo>
  <p>I am &ndash; and I am&nbsp;non-breaking space.</p>
</foo>

并且这个转换:

        <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>

您的新输入将是这样的:

You would have this result as your new input:

<foo>
   <p>I am – and I am non-breaking space.</p>
</foo>

此外,您可以将所有这些定义保存在一个单独的文件中,并像这样添加一个引用:
<!ENTITY % winansi SYSTEM "path/to/my/map/winansi.xml">  %winansi;]>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接