【问题标题】:Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected通过 XmlNamespaceManager 使用默认命名空间的 Xml-SelectNodes 无法按预期工作
【发布时间】:2010-11-24 21:25:28
【问题描述】:

我有一些带有默认命名空间的 xml

<a xmlns='urn:test.Schema'><b/><b/></a>

想统计&lt;b/&gt;的数量

我该如何定义

XmlNamespaceManager nsmgr = ????
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);

使断言变为真?

到目前为止我已经尝试过(使用 nunit):

[Test]
[Ignore("Why does this not work?")]
public void __DoesNotWork_TestSelectWithDefaultNamespace()
{
    // xml to parse with defaultnamespace
    string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    // fails because xpath does not have the namespace
    //!!!!
    Assert.AreEqual(2, doc.SelectNodes("//b").Count);

    // using XPath defaultnamespace 
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
    nsmgr.AddNamespace("", "urn:test.Schema");

    // This will fail with dotnet 3.5sp1. Why?
    //!!!!
    Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
}

[Test]
public void TestSelectWithoutNamespaces_Ok()
{
    // xml to parse without namespace
    string xml = @"<a><b/><b/></a>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    // works ok
    Assert.AreEqual(2, doc.SelectNodes("//b").Count);

    // works ok
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
    Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
}

[Test]
public void TestSelectWithNamespacesPrefixed_Ok()
{
    // xml to parse with defaultnamespace
    string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    // using XPath namespace via alias "t". works ok but xpath is to complicated
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
    nsmgr.AddNamespace("t", doc.DocumentElement.NamespaceURI);
    Assert.AreEqual(2, doc.SelectNodes("//t:b", nsmgr).Count);
}

【问题讨论】:

    标签: c# .net unit-testing xpath


    【解决方案1】:
    // This will fail with dotnet 3.5sp1. Why? 
    //!!!! 
    Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
    

    这是一个常见问题解答。在 XPath 中,任何不带前缀的名称都假定在“无命名空间”中。为了选择属于命名空间的元素,在任何 XPath 表达式中,它们的名称必须以与该命名空间相关联的前缀作为前缀。 AddNamespace() 方法正是为此目的。它在特定命名空间和特定前缀之间创建绑定。那么,如果在 XPath 表达式中使用了这个前缀,那么以它为前缀的元素就可以被选中。

    写在XPath W3C spec中:“节点测试中的QName使用表达式上下文中的命名空间声明扩展为扩展名称。这与扩展的方式相同对于开始和结束标记中的元素类型名称,除了不使用使用 xmlns 声明的默认命名空间:如果 QName 没有前缀,则命名空间 URI 为空”。

    请参阅:w3.org/TR/xpath/#node-tests

    因此,任何无前缀的名称都被视为“无命名空间”。在提供的 XML 文档中,“无命名空间”中没有 b 元素,这就是 XPath 表达式 //b 根本不选择节点的原因。

    使用

    XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
    nsmanager.AddNamespace("x", "urn:test.Schema");
    

    及以后

    Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count);
    

    记住:注册命名空间的全部目的是能够在任何 XPath 表达式中使用前缀(在本例中为 x)。

    【讨论】:

    • 感谢您的回复。您描述的与第三个 unitest TestSelectWithNamespacesPrefixed_Ok() 所做的相同。我仍然希望有一个无需修改 xpath-expression 的解决方法
    • @k3b:你写的我还是希望有一个不需要修改xpath-expression的变通方法。不。这是常见问题解答:没有前缀的 QName 测试选择空(或空)命名空间 URI 中的元素,而不是默认命名空间中的元素。
    • @k3B:不,没有这样的解决方法——它写在 XPath W3C 规范中:“节点测试中的 QName 使用表达式中的命名空间声明扩展为扩展名称上下文。这与对开始和结束标记中的元素类型名称进行扩展的方式相同,只是不使用使用 xmlns 声明的默认命名空间:如果 QName 没有前缀,则命名空间 URI 为空”见此在:w3.org/TR/xpath/#node-tests。因此,任何无前缀的名称都被视为“无命名空间”。 “无命名空间”中没有 b 元素,你得到 0 个节点
    • +1 好答案。 @Alejandro、@Dimitre、@k3b - 正如@Alejandro 之前提到的那样,提及上述关于不带前缀的 QName 测试的陈述仅适用于 XPath 1.0,这可能会有所帮助。在 2.0 中,“一个无前缀的 QName ...在表达式上下文中具有默认元素/类型命名空间的命名空间 URI”(w3.org/TR/xpath20/#node-tests) 这对这个 C# 应用程序没有帮助,因为 XPath 2.0 不可用。但我想避免有人阅读上述内容并得出结论认为没有任何版本的 XPath 允许使用默认命名空间。
    • @LarsH:感谢您提供此信息。我不想提及这一点,因为即使在 XPath 2.0 中,也没有任何方法可以在 XPath 语言中设置默认命名空间——静态上下文的项目只能由宿主语言设置——这不是t 与 XPath 1.0 的当前情况完全不同——向前迈出的唯一一小步是,在使用宿主语言注册命名空间时,可以将命名空间指定为默认命名空间,这意味着任何无前缀的元素名称都在此默认值中命名空间,而不是“无命名空间”。
    猜你喜欢
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-28
    • 2017-03-14
    • 2013-08-22
    相关资源
    最近更新 更多