Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected
I have some xml with default namespace
<a xmlns='urn:test.Schema'><b/><b/></a>
and want to count the number of <b/>
How do I have to define
XmlNamespaceManager ns开发者_开发知识库mgr = ????
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
so that the assert becomes true?
I have tried so far (using 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);
}
// This will fail with dotnet 3.5sp1. Why? //!!!! Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
This is a FAQ. In XPath any unprefixed name is assumed to be in "no namespace". In order to select elements that belong to a namespace, in any XPath expression their names must be prefixed with a prefix that is associated with this namespace. The AddNamespace()
method serves exactly this purpose. It creates a binding between a specific namespace and a specific prefix. Then, if this prefix is used in an XPath expression, the element prefixed by it can be selected.
It is written in the XPath W3C spec: "A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null".
See this at: w3.org/TR/xpath/#node-tests .
So, any unprefixed name is considered to be in "no namespace". In the provided XML document there are no b
elements in "no namespace" and this is why the XPath expression //b
selects no nodes at all.
Use:
XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
nsmanager.AddNamespace("x", "urn:test.Schema");
and later:
Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count);
Remember: The whole purpose of registering the namespace is to be able to use the prefix (in this case x
) in any XPath expression.
精彩评论