Xml-SelectNodes с пространством имен по умолчанию через XmlNamespaceManager не работает должным образом
У меня есть 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 ответ
// This will fail with dotnet 3.5sp1. Why? //!!!! Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);
Это часто задаваемые вопросы. В XPath предполагается, что любое имя без префикса находится в "пространстве имен". Чтобы выбрать элементы, которые принадлежат пространству имен, в любом выражении XPath их имена должны начинаться с префикса, связанного с этим пространством имен. AddNamespace()
Метод служит именно этой цели. Он создает связь между конкретным пространством имен и конкретным префиксом. Затем, если этот префикс используется в выражении XPath, можно выбрать элемент с префиксом.
В спецификации XPath W3C написано: "QName в тесте узла раскрывается в расширенное имя с использованием объявлений пространства имен из контекста выражения. Это аналогично тому, как выполняется расширение для имен типов элементов в начальных и конечных тегах. за исключением того, что пространство имен по умолчанию, объявленное с помощью xmlns, не используется: если у QName нет префикса, то URI пространства имен имеет значение null".
Смотрите это по адресу: http://w3.org/TR/xpath/.
Таким образом, любое имя без префикса считается находящимся в "пространстве имен". В предоставленном 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);
Помните: вся цель регистрации пространства имен состоит в том, чтобы иметь возможность использовать префикс (в этом случае x
) в любом выражении XPath.