Xml-SelectNodes с пространством имен по умолчанию через XmlNamespaceManager не работает должным образом - PullRequest
12 голосов
/ 25 ноября 2010

У меня есть 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 Ответ

29 голосов
/ 25 ноября 2010
// This will fail with dotnet 3.5sp1. Why? 
//!!!! 
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);

Это FAQ .В XPath предполагается, что любое имя без префикса находится в «пространстве имен».Чтобы выбрать элементы, которые принадлежат пространству имен, в любом выражении XPath их имена должны начинаться с префикса, связанного с этим пространством имен.Метод AddNamespace() служит именно этой цели.Он создает связь между конкретным пространством имен и конкретным префиксом.Затем, если этот префикс используется в выражении XPath, можно выбрать элемент с префиксом.

Он записан в спецификации XPath W3C : "AQName в тесте узла раскрывается в расширенное имя с использованием объявлений пространства имен из контекста выражения.Так же как и расширение выполняется для имен типов элементов в начальных и конечных тегах за исключением того, что пространство имен по умолчанию, объявленное с помощью xmlns, не используется: если у QName нет префикса, то URI пространства имен имеет значение null ".

См. Это по адресу: 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);

Помните : вся цель регистрации пространства имен состоит в том, чтобы иметь возможность использовать префикс (в этомcase x) в любом выражении XPath.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...