web-dev-qa-db-ja.com

XPathを使用して既知の正常なxmlノードパスに対してnullを返すSelectSingleNode

この単純なXMLドキュメントを検討してください。ここに示すシリアル化されたXMLは、スキーマを制御できない複雑なPOCOオブジェクトのXmlSerializerの結果です。

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

目標は、idノードの拡張属性の値を抽出することです。この場合、SelectSingleNodeメソッドを使用し、XPath式を次のように指定します。

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;

問題は、特定のXPath式に対してSelectSingleNodeメソッドがnullを返すことです。

質問:このXPathクエリの正確性に関するアイデア、またはこのメソッド呼び出し+ XPath式がnull値を返す理由おそらく、名前空間は問題の一部ですか?

39
p.campbell

問題は名前空間に関係していると強く思う。名前空間を取り除けば大丈夫です-しかし、明らかに、それはドキュメントが修正されたと仮定する実際のケースでは役に立ちません。

XPath式で名前空間を指定する方法を覚えているわけではありませんが、それが問題だと確信しています。

編集:さて、私は今それを行う方法を覚えています。しかし、それはひどく快適ではありません-あなたはXmlNamespaceManagerを作成する必要があります。サンプルドキュメントで使用できるサンプルコードを次に示します。

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);
    }
}
50
Jon Skeet

名前空間を完全に無視したい場合は、これを使用できます:

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

これは名前空間を削除せずにあなたのケースで動作するはずです:

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

名前空間を忘れてしまいました。必要なもの:

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

実際、ここでもWebサービスでも、XPath操作またはXPathに依存するものからnullを取得することは、通常、XML名前空間に問題があることを示しています。

7
John Saunders

まあ...私は同じ問題を抱えていた、それは頭痛だった。名前空間やxmlスキーマについてはあまり気にしていないので、このデータをxmlから削除するだけで、すべての問題が解決しました。最良の答えではないでしょうか?おそらく、しかし、これらすべてに対処したくなくて、データだけに関心がある(そして他のタスクにxmlを使用しない)場合は、名前空間を削除することで問題が解決する可能性があります。

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

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

注意すべきルールは、ドキュメントでnamespaceを指定している場合、SelectNodes()またはSelectSingleNode()への呼び出しでXmlNamespaceManagerを使用する必要があります。それはいい。

記事 名前空間の利点 を参照してください。 Jon Skeetは、XmlNamespaceManagerの使用方法を示す彼の答えで素晴らしい仕事をしています。 (この回答は、実際にはその回答に対するコメントである必要がありますが、コメントするのに十分なRep Pointsがありません。)

1
Erica Ackerman

名前空間の問題を解決するために構築するために、私の場合、複数の名前空間を持つドキュメントに遭遇し、名前空間を適切に処理する必要がありました。ドキュメント内のネームスペースを処理するネームスペースマネージャを取得するために、以下の関数を作成しました。

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

/ idの代わりに// idを使用します。私のコードではうまくいきます

0
dong