使用未定义的实体解析XHTML文档

the*_*eta 6 .net xml vb.net xhtml

在使用Python编码时,如果我必须使用未定义的实体加载XHTML文档,我将创建一个解析器并更新实体dict(即nbsp):

import xml.etree.ElementTree as ET
parser = ET.XMLParser()
parser.entity['nbsp'] = ' '
tree = ET.parse(opener.open(url), parser=parser)
Run Code Online (Sandbox Code Playgroud)

使用VB.Net我尝试将XHTML文档解析为Linq XDocument:

Dim x As XDocument = XDocument.Load(url)
Run Code Online (Sandbox Code Playgroud)

它引发了XmlException:

提及未申报实体'nbsp'

谷歌搜索我找不到任何示例如何更新实体表或使用简单的方法来解析未定义的实体XHTML文档.

如何解决这个看似简单的问题?

Sim*_*ier 3

实体解析由底层解析器完成,这里是一个标准XmlReader(或XmlTextReader)。

正式地,您应该在 DTD 中声明实体(请参阅此处 Oleg 的回答:Problem with XHTMLEntity),或者将 DTD 动态加载到您的文档中。这里有一些关于 SO 的示例,如下所示:How do IresolveEntityswhenloadingtoanXDocument?

您还可以做的是创建一个 hackyXmlTextReader派生类,该派生Text类在检测到实体时基于字典返回节点,就像我在以下示例代码中演示的那样:

using (XmlTextReaderWithEntities reader = new XmlTextReaderWithEntities(MyXmlFile))
{
    reader.AddEntity("nbsp", "\u00A0");
    XDocument xdoc = XDocument.Load(reader);
}

...

public class XmlTextReaderWithEntities : XmlTextReader
{
    private string _nextEntity;
    private Dictionary<string, string> _entities = new Dictionary<string, string>();

    // NOTE: override other constructors for completeness
    public XmlTextReaderWithEntities(string path)
        : base(path)
    {
    }

    public void AddEntity(string entity, string value)
    {
        _entities[entity] = value;
    }

    public override bool Read()
    {
        if (_nextEntity != null)
            return true;

        return base.Read();
    }

    public override XmlNodeType NodeType
    {
        get
        {
            if (_nextEntity != null)
                return XmlNodeType.Text;

            return base.NodeType;
        }
    }

    public override string Value
    {
        get
        {
            if (_nextEntity != null)
            {
                string value = _nextEntity;
                _nextEntity = null;
                return value;
            }
            return base.Value;
        }
    }

    public override void ResolveEntity()
    {
        // if not found, return the string as is
        if (!_entities.TryGetValue(LocalName, out _nextEntity))
        {
            _nextEntity = "&" + LocalName + ";";
        }
        // NOTE: we don't use base here. Depends on the scenario
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法适用于简单的场景,但您可能需要覆盖一些其他内容以确保完整性。

PS:抱歉,这是 C# 语言,您必须适应 VB.NET :)