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

k3b*_*k3b 13 .net c# xpath unit-testing

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

<a xmlns='urn:test.Schema'><b/><b/></a>
Run Code Online (Sandbox Code Playgroud)

并想要数数 <b/>

我该如何定义

XmlNamespaceManager nsmgr = ????
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
Run Code Online (Sandbox Code Playgroud)

断言变为真?

我到目前为止尝试过(使用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);
}
Run Code Online (Sandbox Code Playgroud)

Dim*_*hev 30

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

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

它是用XPath W3C规范编写的:"节点测试中的QName使用表达式上下文中的命名空间声明扩展为扩展名.这与开始和结束标记中的元素类型名称的扩展相同除了没有使用xmlns声明的默认命名空间:如果QName没有前缀,那么命名空间URI为空".

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

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

使用:

XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
nsmanager.AddNamespace("x", "urn:test.Schema");
Run Code Online (Sandbox Code Playgroud)

然后:

Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count);
Run Code Online (Sandbox Code Playgroud)

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

  • +1好的答案.@Alejandro,@ Dimitre,@ k3b - 正如@Alejandro之前提到的那样,提及有关没有前缀的QName测试的上述陈述仅适用于XPath 1.0可能会有所帮助.在2.0中,"一个没有前缀的QName ...在表达式上下文中具有默认元素/类型名称空间的名称空间URI"(http://www.w3.org/TR/xpath20/#node-tests)那不会帮助这个C#应用程序,因为XPath 2.0不可用.但是我想避免某人阅读上述内容并得出结论,没有版本的XPath允许使用默认命名空间. (5认同)
  • @ k3b:你写了*我仍然希望有一个workaroud而不需要修改xpath-expression*.不.这是FAQ:没有前缀的QName测试选择null(或空)命名空间URI中的元素,而不是默认命名空间. (4认同)
  • @LarsH:谢谢你提出这个问题.我不想提这个,因为即使在XPath 2.0中也没有办法在XPath语言中设置默认命名空间 - 静态上下文的项只能通过托管语言设置 - 这不是'与XPath 1.0的当前情况不同 - 向前迈出的唯一一小步是在托管语言中注册命名空间时,可以将命名空间指定为默认命名空间,这意味着任何未加前缀的元素名称都在此默认值中命名空间而不是"无命名空间". (3认同)