wake-up-neo.com

SelectSingleNode gibt mit XPath einen bekanntermaßen guten XML-Knotenpfad zurück

Betrachten Sie dieses einfache XML-Dokument. Das hier gezeigte serialisierte XML ist das Ergebnis eines XmlSerializers aus einem komplexen POCO-Objekt, auf dessen Schema ich keinen Einfluss habe.

<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>

Das Ziel besteht darin, den Wert des Erweiterungsattributs im ID-Knoten zu extrahieren. In diesem Fall verwenden wir die SelectSingleNode-Methode und erhalten einen XPath-Ausdruck als solchen:

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;

Das Problem ist, dass die SelectSingleNode-Methode für den angegebenen XPath-Ausdruck Null zurückgibt.

Frage: irgendwelche Ideen zur Korrektheit dieser XPath-Abfrage oder warum diese Methode + XPath-Ausdruck aufrufen würde, würde einen Nullwert zurückgeben? Vielleicht sind die Namespaces Teil des Problems?

36
p.campbell

Ich vermute stark, dass das Problem mit Namespaces zusammenhängt. Versuchen Sie, den Namespace zu entfernen, und Sie sind in Ordnung - aber das hilft natürlich nicht, wenn das Problem behoben ist.

Ich kann mich nicht ohne weiteres daran erinnern, wie man einen Namespace in einem XPath-Ausdruck angibt, aber ich bin mir sicher, dass dies das Problem ist.

EDIT: Okay, ich habe mich daran erinnert, wie ich es jetzt mache. Es ist zwar nicht besonders angenehm - Sie müssen dafür ein XmlNamespaceManager erstellen. Hier ist ein Beispielcode, der mit Ihrem Beispieldokument funktioniert:

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

Wenn Sie Namespaces vollständig ignorieren möchten, können Sie Folgendes verwenden:

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

Dies sollte in Ihrem Fall funktionieren, ohne Namespaces zu entfernen: 

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

Entschuldigung, Sie haben den Namespace vergessen. Du brauchst:

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

Ob hier oder in Webdiensten, das Abrufen von NULL von einem XPath-Vorgang oder etwas, das von XPath abhängig ist, weist in der Regel auf ein Problem mit XML-Namespaces hin.

7
John Saunders

Nun, ich hatte das gleiche Problem und es hatte Kopfschmerzen. Da mir der Namespace oder das XML-Schema nicht besonders wichtig war, löschte ich diese Daten einfach aus meiner XML-Datei und löste alle meine Probleme. Kann nicht die beste Antwort sein? Wahrscheinlich, aber wenn Sie sich nicht mit all dem befassen wollen und sich NUR für die Daten interessieren (und die XML-Datei nicht für andere Aufgaben verwenden), kann das Löschen des Namespaces Ihre Probleme lösen.

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

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

Die Regel, die Sie beachten sollten: Wenn Ihr Dokument eine namespace angibt, MÜSSEN Sie in Ihrem Aufruf von SelectNodes() oder SelectSingleNode() eine XmlNamespaceManager verwenden. Das ist gut. 

Siehe den Artikel Vorteile von Namespaces . Jon Skeet macht seine Antwort großartig und zeigt, wie man XmlNamespaceManager verwendet. (Diese Antwort sollte eigentlich nur ein Kommentar zu dieser Antwort sein, aber ich habe nicht genügend Rep-Punkte zum Kommentieren.)

0
Erica Ackerman

verwenden Sie einfach // id anstelle von/id. Es funktioniert gut in meinem Code

0
dong

Um auf die Lösung der Namespace-Probleme hinzuarbeiten, habe ich in meinem Fall Dokumente mit mehreren Namespaces ausgeführt und musste Namespaces richtig behandeln. Ich habe die Funktion unten geschrieben, damit ein Namespace-Manager mit jedem Namespace im Dokument umgehen kann:

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;
    }