web-dev-qa-db-fra.com

SelectSingleNode renvoyant la valeur null pour le bon chemin de nœud XML connu avec XPath

Considérez ce document XML simple. Le XML sérialisé présenté ici est le résultat d'un XmlSerializer à partir d'un objet POCO complexe dont je n'ai aucun contrôle sur le schéma.

<My_RootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="">
  <id root="2.16.840.1.113883.3.51.1.1.1" extension="someIdentifier" xmlns="urn:hl7-org:v3" /> 
  <creationTime xsi:nil="true" xmlns="urn:hl7-org:v3" />      
</My_RootNode>

L'objectif est d'extraire la valeur de l'attribut extension sur le nœud id. Dans ce cas, nous utilisons la méthode SelectSingleNode et donnons une expression XPath en tant que telle:

XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/id");
//idNode is evaluated to null at this point in the debugger!
string msgID = idNode.Attributes.GetNamedItem("extension").Value;

Le problème est que la méthode SelectSingleNode renvoie null pour l'expression XPath donnée.

Question: des idées sur l'exactitude de cette requête XPath, ou pourquoi cet appel de méthode + l'expression XPath retournerait-il une valeur nulle? Peut-être que les espaces de noms font partie du problème?

36
p.campbell

Je soupçonne fortement que le problème est lié aux espaces de noms. Essayez de vous débarrasser de l'espace de noms et tout ira bien, mais cela ne vous aidera évidemment pas dans votre cas réel, dans lequel je supposerais que le document est corrigé.

Je ne me souviens pas très tôt comment spécifier un espace de nom dans une expression XPath, mais je suis sûr que c'est le problème.

EDIT: D'accord, je me suis rappelé comment faire maintenant. Ce n'est pas très agréable cependant - vous devez créer une XmlNamespaceManager pour cela. Voici un exemple de code qui fonctionne avec votre exemple de document:

using System;
using System.Xml;

public class Test
{
    static void Main()
    {
        XmlDocument doc = new XmlDocument();
        XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
        namespaces.AddNamespace("ns", "urn:hl7-org:v3");
        doc.Load("test.xml");
        XmlNode idNode = doc.SelectSingleNode("/My_RootNode/ns:id", namespaces);
        string msgID = idNode.Attributes["extension"].Value;
        Console.WriteLine(msgID);
    }
}
44
Jon Skeet

Si vous voulez ignorer complètement les espaces de noms, vous pouvez utiliser ceci:

static void Main(string[] args)
{
    string xml =
        "<My_RootNode xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"\">\n" +
        "    <id root=\"2.16.840.1.113883.3.51.1.1.1\" extension=\"someIdentifier\" xmlns=\"urn:hl7-org:v3\" />\n" +
        "    <creationTime xsi:nil=\"true\" xmlns=\"urn:hl7-org:v3\" />\n" +
        "</My_RootNode>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    XmlNode idNode = doc.SelectSingleNode("/*[local-name()='My_RootNode']/*[local-name()='id']");
}
12
mrzli

Cela devrait fonctionner dans votre cas sans supprimer les espaces de noms: 

XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];
8
tandrasz

Désolé, vous avez oublié l'espace de noms. Vous avez besoin:

XmlNamespaceManager ns = new XmlNamespaceManager(myXmlDoc.NameTable);
ns.AddNamespace("hl7","urn:hl7-org:v3");
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/hl7:id", ns);

En fait, que ce soit ici ou dans les services Web, le retour à zéro d'une opération XPath ou de tout ce qui dépend de XPath indique généralement un problème lié aux espaces de noms XML.

7
John Saunders

Eh bien ... j'ai eu le même problème et c'était un mal de tête. Comme je ne me souciais pas beaucoup de l’espace de noms ou du schéma xml, j’ai simplement supprimé ces données de mon xml et cela a résolu tous mes problèmes. Peut-être pas la meilleure réponse? Probablement, mais si vous ne voulez pas gérer tout cela et que vous vous souciez UNIQUEMENT des données (et que vous n'utiliserez pas le xml pour une autre tâche), la suppression de l'espace de noms peut résoudre vos problèmes.

XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);

vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");
2
Roisgoen

La règle à garder à l'esprit est la suivante: si votre document spécifie une namespace, vous DEVEZ utiliser une XmlNamespaceManager dans votre appel à SelectNodes() ou à SelectSingleNode(). C'est une bonne chose. 

Voir l'article Avantages des espaces de noms . Jon Skeet fait un excellent travail dans sa réponse en montrant comment utiliser XmlNamespaceManager. (Cette réponse ne devrait en réalité être qu’un commentaire sur cette réponse, mais je n’ai pas tout à fait assez de points de reps pour commenter.)

0
Erica Ackerman

utilisez simplement // id au lieu de/id. Cela fonctionne bien dans mon code

0
dong

Juste pour continuer à résoudre les problèmes d’espace de noms, dans mon cas, j’ai été confronté à des documents comportant plusieurs espaces de noms et nécessaires pour les gérer correctement. J'ai écrit la fonction ci-dessous pour qu'un gestionnaire d'espaces de noms traite tout espace de noms dans le document:

private XmlNamespaceManager GetNameSpaceManager(XmlDocument xDoc)
    {
        XmlNamespaceManager nsm = new XmlNamespaceManager(xDoc.NameTable);
        XPathNavigator RootNode = xDoc.CreateNavigator();
        RootNode.MoveToFollowing(XPathNodeType.Element);
        IDictionary<string, string> NameSpaces = RootNode.GetNamespacesInScope(XmlNamespaceScope.All);

        foreach (KeyValuePair<string, string> kvp in NameSpaces)
        {
            nsm.AddNamespace(kvp.Key, kvp.Value);
        }

        return nsm;
    }
0