2010-11-24 28 views
10

J'ai quelques xml avec espace de noms par défautXml-SelectNodes avec défaut-espace de noms via XmlNamespaceManager ne fonctionne pas comme prévu

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

et que vous souhaitez compter le nombre de <b/>

Comment dois-je définir

XmlNamespaceManager nsmgr = ???? 
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); 

afin que l'affirmation devienne vraie?

Je l'ai essayé jusqu'à présent (en utilisant 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); 
} 

Répondre

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

C'est une FAQ. Dans XPath, tout nom non préfixé est supposé être dans "aucun espace de noms". Afin de sélectionner les éléments qui appartiennent à un espace de noms, dans toute expression XPath, leurs noms doivent être préfixés avec un préfixe associé à cet espace de noms. La méthode AddNamespace() sert exactement ce but. Il crée une liaison entre un espace de noms spécifique et un préfixe spécifique. Ensuite, si ce préfixe est utilisé dans une expression XPath, l'élément préfixé par celui-ci peut être sélectionné.

Il est écrit dans le XPath W3C spec:. « Un QName dans le test de nœud est développé en un nom étendu en utilisant les déclarations d'espace de noms à partir du contexte d'expression C'est la même extension ainsi est fait pour les noms de type d'élément dans début et tags de fin, sauf que l'espace de noms par défaut déclaré avec xmlns n'est pas utilisé: si le QName n'a pas de préfixe, alors l'URI d'espace de nommage est nul ".

Voir ceci: w3.org/TR/xpath/#node-tests. Par conséquent, tout nom non préfixé est considéré comme étant dans "aucun espace de noms". Dans le document XML fourni, il n'y a pas d'éléments b dans "aucun espace de noms" et c'est pourquoi l'expression XPath //b ne sélectionne aucun noeud.

Utilisation:

XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable); 
nsmanager.AddNamespace("x", "urn:test.Schema"); 

et plus tard:

Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count); 

Rappelez-vous: Le but de l'enregistrement de l'espace de noms est d'être en mesure d'utiliser le préfixe (dans ce cas x) dans n'importe quelle expression XPath.

+0

merci pour votre réponse. vous avez décrit la même chose que ce que fait le troisième unitest TestSelectWithNamespacesPrefixed_Ok(). j'espère toujours qu'il y a un workaroud sans avoir besoin de modifier l'expression xpath – k3b

+4

@ k3b: Vous avez écrit * j'espère toujours qu'il y a un workaroud sans avoir besoin de modifier l'expression xpath *. Non. Ceci est une FAQ: un test QName sans préfixe sélectionne des éléments dans l'URI d'espace de noms nul (ou vide), pas dans l'espace de noms par défaut. –

+0

@ k3B: Non, il n'y a pas de solution de contournement - Il est écrit dans la spécification XPath W3C: "Un QName dans le test de nœud est développé en un nom étendu en utilisant les déclarations d'espace de noms du contexte d'expression.C'est la même chose que pour les noms de types d'éléments dans les balises start et end sauf que l'espace de noms par défaut déclaré avec xmlns n'est pas utilisé: si le QName n'a pas de préfixe, l'URI d'espace de nommage est nul. http://www.w3.org/TR/xpath/#node-tests Ainsi, tout nom non préfixé est considéré comme étant dans "no namespace" Il n'y a pas d'éléments 'b' dans" no namespace "et vous obtenez 0 nœuds –