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

时间:2014-04-07 18:30:55

标签: .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)

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

Dim x As XDocument = XDocument.Load(url)

引发了XmlException:

  

参考未声明的实体'nbsp'

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

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

3 个答案:

答案 0 :(得分:2)

实体解析由底层解析器完成,该解析器在此为标准XmlReader(或XmlTextReader)。

正式地,您应该在DTD中声明实体(请参阅Oleg的答案:Problem with XHTML entities),或者将DTD动态加载到您的文档中。这里有一些例子,如:How do I resolve entities when loading into an XDocument?

您还可以创建一个hacky XmlTextReader派生类,在检测到实体时返回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
    }
}

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

PS:抱歉,在C#中,你必须适应VB.NET:)

答案 1 :(得分:1)

我还没有这样做,但您可以创建一个XmlParserContext对象,其中包含所需的实体声明internalSubset。将该上下文传递给构造函数中的XmlTextReader,并通过加载阅读器创建XDocument对象。在MSDN中,VB中已有一个简单的example code snippet用于使用预定义的实体。

答案 2 :(得分:0)

在这种情况下,我认为您在网络上浏览了一个页面,因此您可以使用可满足您需求的html敏捷包。

我使用xpath,element和更多其他东西。搜索html页面等非常有用。

您可以在此处找到文档:htmlagilitypack