具有默认命名空间的Xml-SelectNodes通过XmlNamespaceManager无法按预期工作

时间:2010-11-24 21:25:29

标签: c# .net unit-testing xpath

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

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

并想要计算<b/>

的数量

我如何定义

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);
}

1 个答案:

答案 0 :(得分:28)

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

这是常见问题解答。在XPath中,任何未加前缀的名称都假定为“无命名空间”。为了选择属于命名空间的元素,在任何XPath表达式中,它们的名称必须以与此命名空间关联的前缀作为前缀。 AddNamespace()方法恰好用于此目的。它在特定命名空间和特定前缀之间创建绑定。然后,如果在XPath表达式中使用此前缀,则可以选择前缀为它的元素。

它写在 XPath W3C spec 中:“节点测试中的QName使用表达式上下文中的命名空间声明扩展为扩展名。这与扩展相同对于start和end-tags中的元素类型名称,除了不使用xmlns声明的默认名称空间外:如果QName没有前缀,则名称空间URI为null“。

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

因此,任何未加前缀的名称都被视为“无命名空间”。在提供的XML文档中,“no namespace”中没有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)。