76 votes

Pourquoi XmlNamespaceManager est-il nécessaire?

J'ai trouvé un peu sec comme pour pourquoi , tout au moins dans le .Net Framework -- il est nécessaire d'utiliser un XmlNamespaceManager afin de gérer les espaces de noms (ou plutôt maladroit et détaillé [local-name()=... XPath prédicat/fonction/whatever) lors de l'exécution de requêtes XPath. Je ne comprends pourquoi les espaces de noms sont nécessaires ou du moins bénéfique, mais pourquoi est-il si complexe?

Pour l'interrogation d'un Document XML simple (pas d'espaces de noms)...

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
   <nodeName>Some Text Here</nodeName>
</rootNode>

...on peut utiliser quelque chose comme doc.SelectSingleNode("//nodeName") (qui correspond <nodeName>Some Text Here</nodeName>)

Mystère #1: Mon premier gêne -- Si je comprends bien, est-ce simplement l'ajout d'une référence d'espace de noms pour les parents/balise racine (si utilisé comme partie d'un nœud enfant de la balise ou pas) comme suit:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://someplace.org">
   <nodeName>Some Text Here</nodeName>
</rootNode>

...nécessite plusieurs lignes de code supplémentaires pour obtenir le même résultat:

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://s+omeplace.org")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)

...de fait, l'idée d'un inexistante préfixe ("ab") pour trouver un nœud qui n'a même pas d'utiliser un préfixe. Comment cela fait-il sens? Ce qui est faux (conceptuellement) avec doc.SelectSingleNode("//nodeName")?

Mystère #2: Donc, disons que vous avez un document XML qui utilise les préfixes:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
   <cde:nodeName>Some Text Here</cde:nodeName>
   <feg:nodeName>Some Other Value</feg:nodeName>
   <feg:otherName>Yet Another Value</feg:otherName>
</rootNode>

... Si je comprends bien, vous devez ajouter les deux espaces à l' XmlNamespaceManager, afin de faire une requête pour un seul nœud...

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://someplace.org")
nsmgr.AddNamespace("feg", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)

... Pourquoi, dans ce cas, ai-je besoin d' (conceptuellement) un espace de noms manager?

**RÉDIGÉ dans les commentaires ci-dessous**

Edit Ajouté: Mon révisé et affiné question est basée sur l'apparente redondance de la XmlNamespaceManager dans ce que je crois être la majorité des cas et l'utilisation de l'espace de noms du gestionnaire de spécifier une correspondance de préfixe à URI:

Lorsque le mappage direct du préfixe d'espace de noms ("cde") à l'espace de noms URI ("http://someplace.org") il est expressément indiqué dans le document source:

...<rootNode xmlns:cde="http://someplace.org"...

qu'est-ce que le conceptuel avoir besoin d'un programmeur pour recréer cette cartographie avant de faire une requête?

21voto

Paul Butcher Points 5271

Le point de base (comme l'a souligné Kev, ci-dessus), c'est que l'URI d'espace de noms est la partie importante de l'espace de noms, plutôt que de le préfixe d'espace de noms, le préfixe est un "arbitraire commodité"

Quant à savoir pourquoi vous avez besoin d'un espace de noms du gestionnaire, sans qu'il y ait un peu de magie qui fonctionne en utilisant le document, je pense à deux raisons.

Raison 1

S'il était permis de n'ajouter que des déclarations d'espace de noms pour le documentElement, comme dans votre exemple, il serait en effet trivial pour selectSingleNode à l'utiliser tout ce qui est défini.

Toutefois, vous pouvez définir des préfixes d'espace de noms sur n'importe quel élément dans un document, et les préfixes d'espaces de noms ne sont pas uniquement liées à un espace de noms donné dans un document. Considérons l'exemple suivant

<w xmlns:a="mynamespace">
  <a:x>
    <y xmlns:a="myOthernamespace">
      <z xmlns="mynamespace">
      <b:z xmlns:b="mynamespace">
      <z xmlns="myOthernamespace">
      <b:z xmlns:b="myOthernamespace">
    </y>
  </a:x>
</w>

Dans cet exemple, que voudriez-vous //z, //a:z et //b:z de retour? Comment, sans une sorte d'espace de noms externe gestionnaire, voulez-vous exprimer?

Raison 2

Il vous permet de réutiliser la même expression XPath pour tout document équivalent, sans avoir besoin de tout savoir sur les préfixes d'espace de noms en cours d'utilisation.

myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);

doc1:

<x>
  <z:y xmlns:z="mynamespace" />
</x>

doc2:

<x xmlns"mynamespace">
  <y>
</x>

Afin d'atteindre ce dernier objectif, sans un espace de noms manager, vous devez inspecter chaque document, la construction d'une coutume de l'expression XPath pour chacun.

14voto

AZ. Points 3712

La raison en est simple. Il n'y a pas de connexion entre les préfixes que vous utilisez dans votre requête XPath et la déclaration de préfixes dans le document xml. Pour donner un exemple à la suite de xmls sont sémantiquement équivalentes:

<aaa:root xmlns:aaa="http://someplace.org">
 <aaa:element>text</aaa:element>
</aaa:root>

vs

  <bbb:root xmlns:bbb="http://someplace.org">
     <bbb:element>text</bbb:element>
  </bbb:root>

Le "ccc:root/ccc:element" requête match les deux cas, à la condition que la cartographie de l'espace gestionnaire.

nsmgr.AddNamespace("ccc", "http://someplace.org")

L' .NET mise en œuvre ne se soucie pas de la littéralité des préfixes utilisés dans le xml mais seulement qu'il est un préfixe défini pour la requête littérale et que l'espace de noms valeur correspond à la valeur réelle de la doc. C'est nécessaire d'avoir constamment les expressions de la requête, même si les préfixes varient entre consommé des documents et la mise en œuvre correcte pour le cas général.

14voto

Jez Points 4075

Aussi loin que je peux dire, il n'y a aucune raison que vous devriez avoir besoin de définir manuellement un XmlNamespaceManager d'obtenir au abc-préfixés nœuds si vous avez un document comme ceci:

<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com">
    <abc:nodeA>...</abc:nodeA>
    <def:nodeB>...</def:nodeB>
    <abc:nodeC>...</abc:nodeC>
</itemContainer>

Microsoft ne pouvait tout simplement pas être pris la peine d'écrire quelque chose de détecter qu' xmlns:abc avait déjà été précisé dans un nœud parent. J'ai peut-être tort, et si oui, je serais heureux de recevoir vos commentaires sur cette réponse afin que je puisse les mettre à jour.

Cependant, ce blog semble confirmer mes soupçons. Il dit essentiellement que vous avez besoin de définir un XmlNamespaceManager manuellement et de parcourir l' xmlns: attributs, l'ajout de chacun à l'espace de noms du gestionnaire. Je ne sais pas pourquoi Microsoft ne pouvait pas faire cela automatiquement.

Voici une méthode que j'ai créée à partir de ce post de blog pour générer automatiquement un XmlNamespaceManager basé sur l' xmlns: attributs d'une source d' XmlDocument:

/// <summary>
/// Creates an XmlNamespaceManager based on a source XmlDocument's name table, and prepopulates its namespaces with any 'xmlns:' attributes of the root node.
/// </summary>
/// <param name="sourceDocument">The source XML document to create the XmlNamespaceManager for.</param>
/// <returns>The created XmlNamespaceManager.</returns>
private XmlNamespaceManager createNsMgrForDocument(XmlDocument sourceDocument)
{
    XmlNamespaceManager nsMgr = new XmlNamespaceManager(sourceDocument.NameTable);

    foreach (XmlAttribute attr in sourceDocument.SelectSingleNode("/*").Attributes)
    {
        if (attr.Prefix == "xmlns")
        {
            nsMgr.AddNamespace(attr.LocalName, attr.Value);
        }
    }

    return nsMgr;
}

Et je l'utilise comme ceci:

XPathNavigator xNav = xmlDoc.CreateNavigator();
XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));

4voto

Kev Points 60744

J'ai la réponse au point 1:

Définition d'un espace de noms par défaut pour un document XML signifie encore que les nœuds, même sans un préfixe d'espace de noms, c'est à dire:

<rootNode xmlns="http://someplace.org">
   <nodeName>Some Text Here</nodeName>
</rootNode>

ne sont plus dans le "vide" de l'espace de noms. Vous avez encore besoin d'une certaine manière de faire référence à ces nœuds à l'aide de XPath, afin de vous créer un préfixe de référence, même si elle est "faite".

Pour répondre au point 2:

<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
   <cde:nodeName>Some Text Here</cde:nodeName>
   <feg:nodeName>Some Other Value</feg:nodeName>
   <feg:otherName>Yet Another Value</feg:otherName>
</rootNode>

En interne dans le document d'instance, les nœuds qui se trouvent dans un espace de noms sont stockés avec leur nom de nœud et de leur longue durée de nom d'espace de noms, il est appelé (dans le langage W3C) un nom augmenté.

Par exemple <cde:nodeName> est essentiellement stockées en tant que <http://someplace.org:nodeName>. Un préfixe d'espace de noms est un arbitraire de la commodité pour les humains, de sorte que lorsque nous type XML ou le lire nous n'avons pas à le faire:

<rootNode>
   <http://someplace.org:nodeName>Some Text Here</http://someplace.org:nodeName>
   <http://otherplace.net:nodeName>Some Other Value</http://otherplace.net:nodeName>
   <http://otherplace.net:otherName>Yet Another Value</http://otherplace.net:otherName>
</rootNode>

Lorsqu'un document XML est recherché, ce n'est pas recherché par l'amicale préfixe, la recherche se fait par URI d'espace de noms, de sorte que vous avez à dire XPath sur vos espaces de noms par l'intermédiaire d'un espace de noms de table passées à l'aide de XmlNamespaceManager.

3voto

Christian Schwarz Points 175

Vous devez vous inscrire à l'URI/préfixe paires à la XmlNamespaceManager exemple de laisser SelectSingleNode() de savoir qui, notamment, "nodeName" nœud que vous faites référence à l'un des "http://someplace.org" ou celle de "http://otherplace.net".

Veuillez noter que le béton préfixe le nom n'a pas d'importance lorsque vous êtes en train de faire la requête XPath. Je crois que cela marche aussi:

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("any", "http://someplace.org")
nsmgr.AddNamespace("thing", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//thing:nodeName", nsmgr)

SelectSingleNode() juste besoin d'une connexion entre le préfixe de votre expression XPath et l'URI d'espace de noms.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X