我需要将 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,如 this answer 所示。

但是,XHTML5 没有 DTD,正如在 this other answer 中讨论的那样。其实体定义仅供引用 as JSON
<!DOCTYPE html>

因此,在 XHTML5 中解析实体时,永远不会调用 XmlResolver 方法。有关于 providing XmlReader with a list of entity declarations 尝试的讨论,但似乎没有任何方法是开箱即用的。

目前,我正在研究两种方法。第一个是通过对源 XHTML 的字符串操作或通过 XmlParserContext.InternalSubset 指定具有文档类型声明中的实体声明的内部子集。这将导致类似于以下内容的文档类型声明:
<!DOCTYPE html [
  <!ENTITY ndash "&#8211;">
  <!ENTITY nbsp "&#160;">
  ...
]>

在 XHTML5 中似乎是这个 is allowed;然而,这是不可取的,因为它将 XDocument 与实体声明(现在有 more than 2000 )一起乱扔,如果用户将其转换回字符串表示形式,这将是有问题的。

我的另一种方法是使用正则表达式预处理 XHTML 字符串,将所有命名字符引用转换为数字字符引用(或实际的 Unicode 字符),不包括 XML 预定义实体 " & ' < > 。但是,我担心这种方法可能会遗漏 XML 定义中的复杂性。例如,this answer 表示不得在注释、CDATA 部分或处理指令中对字符进行转义。我假设我的正则表达式需要调整以排除所有这些事件。

有没有人对这两种方法或您考虑的任何其他方法有经验或建议?我更喜欢建立在 XmlReader 的可扩展性基础上的方法,但如果没有其他方法,我将求助于源字符串操作。

最佳答案

如果您将身份转换应用到具有实体映射的源文档,它将在结果中替换您的实际字符。对我来说,这与正则表达式没有什么不同(一步),当然也不那么复杂。

鉴于此来源:

<!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>

您会将这个结果作为您的新输入:
<foo>
   <p>I am – and I am non-breaking space.</p>
</foo>

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

关于c# - 将 XHTML5 解析为 XDocument,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35520594/

10-11 00:55