为什么 XmlNamespaceManager return 与 HasNamespace 的结果不一致?

Why does XmlNamespaceManager return inconsistent results for HasNamespace?

我正在尝试处理 XML 文档并确定其中定义了哪些命名空间,但我无法从 XmlNamespaceManager.HasNamespace 获得一致的结果。当它正在阅读文档时,HasNamespace 将 return false 即使它仍然被声明并在范围内。

示例代码:

    var ctx = new XmlParserContext(null, new XmlNamespaceManager(new NameTable()), null, XmlSpace.None);
    var set = new XmlReaderSettings() { IgnoreComments = true, IgnoreProcessingInstructions = true, IgnoreWhitespace = true };

    using (var xml = new StringReader(
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
        "<rdf:RDF " +
        "  xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"> " +
        "  <rdf:Description rdf:about=\"x\" /> " +
        "</rdf:RDF>"))
    using (var rdr = XmlReader.Create(xml, set, ctx))
    {
        rdr.MoveToContent();

        Console.WriteLine(rdr.Name);
        Console.WriteLine(rdr.LookupNamespace("rdf"));
        Console.WriteLine(ctx.NamespaceManager.HasNamespace("rdf"));    // True
        
        rdr.Read();
        
        Console.WriteLine(rdr.Name);
        Console.WriteLine(rdr.LookupNamespace("rdf"));
        Console.WriteLine(ctx.NamespaceManager.HasNamespace("rdf"));    // False
        
        rdr.Read();
        
        Console.WriteLine(rdr.Name);
        Console.WriteLine(rdr.LookupNamespace("rdf"));
        Console.WriteLine(ctx.NamespaceManager.HasNamespace("rdf"));    // True
    }

Fiddle

当 reader 进入每个新元素时,它会调用命名空间管理器上的 PushScope。一旦它离开元素(通过自闭合标签或相应的结束标签的末尾),它就会调用 PopScope.

HasNamespace,不像命名空间管理器的其他一些成员,只回答当前范围的问题。

Gets a value indicating whether the supplied prefix has a namespace defined for the current pushed scope.

(我的重点

一般来说,你不应该那么多地使用命名空间前缀,除非你实际上是在自己执行解析1 而不是利用现有工具。它是元素名称 (RDF) 和命名空间 (http://www.w3.org/1999/02/22-rdf-syntax-ns#\) 的组合,唯一定义了元素的类型 - 前缀可以更改(前提是它在整个文档范围内一致地完成它是有效的)而不改变 XML.

信息内容

如果你创建这个,你可以自己看到这个class:

class LoggingNamespaceManager : XmlNamespaceManager
{
    public LoggingNamespaceManager (XmlNameTable table) : base(table)
    {

    }

    public override void PushScope()
    {
        Console.WriteLine("Push");
        base.PushScope();
    }

    public override bool PopScope()
    {
        Console.WriteLine("Pop");
        return base.PopScope();
    }
}

并在示例的第一行实例化它而不是 XmlNamespaceManager


1请不要。已经有足够多的脆弱的“XML”解析器,它们是建立在关于 XML 的无效假设之上的。像您目前所做的那样使用框架中提供的工具。