いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

名前空間を指定してXPathを実行する。(XmlNamespaceManager, XmlReader, XPathDocument, XPathNavigator, Evaluate)

XPathを用いたデータの操作方法については、以前 http://d.hatena.ne.jp/gsf_zero1/20070117/p2http://d.hatena.ne.jp/gsf_zero1/20060830/p1 にて、記述しましたが
この時記述したのは、該当のXMLデータがデフォルトの名前空間に属している場合の処理の方法です。


実際のXMLデータは、xmlnsに特定の名前空間を指定しているパターンも結構多いです。
この場合は、XPathを使用する際に、名前空間をマッピングする作業が発生します。


名前空間のマッピングに使用するクラスは、以下のクラスとなります。

System.Xml.XmlNamespaceManager


以下のようにして利用します。

//
// XmlNamespaceManagerを作成。
// コンストラクタに渡すのは、XmlReaderやXPathNavigatorが持っているNameTableを指定します。
//
XmlNamespaceManager nsManager = new XmlNamespaceManager(xpathNavigator.NameTable);

// 名前空間のマッピングを追加.
// 第一引数が使用するプレフィックスで、第二引数がそれに対応する名前空間の値です。
nsManager.AddNamespace("p", "urn:gsf-samples-people.xsd");

// 名前空間のマッピングオブジェクト付きで、XPathを評価.
xpathNavigator.Evaluate("//p:Person/p:Name/text()", nsManager);


XPathを使用する際は、名前空間を指定する場合は必ずXmlNamespaceManagerにてプレフィックスを
つけてのマッピングが必要になります。つまり、基底の名前空間を書き換えている場合も必ずなんらかの
プレフィックスをつけて、登録する必要があります。
たとえば、XMLファイルにて

<People xmlns="xxxxxx">

のように定義している場合は、以下のようにしてなんらかのプレフィックスを定義しておき、それでアクセスします。

nsManager.AddNamespace("p", "xxxxxx");


以下サンプルです。

今回は、XSDとXMLを用意して、それをXPathで取得してみます。

まず、XSDファイルです。

<?xml version="1.0" encoding="shift-jis"?>
<!-- vim:set ts=4 sw=4 et ws is nowrap ft=xsd -->

<xs:schema 
    targetNamespace="urn:gsf-samples-people.xsd" 
    elementFormDefault="qualified"
    xmlns="urn:gsf-samples-people.xsd" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="People">
        <xs:complexType>
            <xs:choice maxOccurs="unbounded">
                <xs:element name="Person">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="Name" type="xs:string"/>
                            <xs:element name="Age" type="xs:int"/>
                            <xs:element name="Address" type="xs:string"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:choice>
        </xs:complexType>
    </xs:element>
</xs:schema>

このスキーマに従って作成された次のXMLファイルが存在するとします。

<?xml version="1.0" encoding="shift-jis"?>
<!-- vim:set ts=4 sw=4 et ws is nowrap ft=xml: -->

<People xmlns="urn:gsf-samples-people.xsd">
    <Person>
        <Name>gsf_zero1</Name>
        <Age>27</Age>
        <Address>日本のどこか</Address>
    </Person>
    <Person>
        <Name>gsf_zero2</Name>
        <Age>28</Age>
        <Address>日本のどこか-2</Address>
    </Person>
    <Person>
        <Name>gsf_zero3</Name>
        <Age>29</Age>
        <Address>日本のどこか-3</Address>
    </Person>
    <Person>
        <Name>gsf_zero4</Name>
        <Age>30</Age>
        <Address>日本のどこか-4</Address>
    </Person>
</People>

最後に、サンプルクラスです。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

namespace CSTest{
    
    public class XPathWithNamespaceSample{

        public void Execute(){
            //
            // XPathを使用してデータを取得する。
            //
            // XMLにxmlnsとしてデフォルト以外の名前空間が指定されている場合は
            // XPathを利用してデータを取得する際に、その名前空間のマッピングを
            // XmlNamespaceManagerインスタンスに追加しておく必要がある。
            //
            // また、XPathを利用する際は、必ずプレフィックスが必要となる。
            //
            using(StreamReader reader = new StreamReader("People.xml")){
                using(XmlReader xmlReader = XmlReader.Create(reader)){
                    //
                    // XPathNavigatorを作成.
                    //
                    XPathNavigator navigator = new XPathDocument(xmlReader).CreateNavigator();

                    //
                    // 名前空間のマッピングを作成する。
                    // コンストラクタには、実際に読み込みを担当するオブジェクトの名称テーブルを指定する。
                    //
                    // 今回のように、XMLにてデフォルトのxmlnsを独自の名前空間に指定している場合は
                    // 名前空間を指定せずにXPathで取得しようとすると何もデータが取得できないので
                    // かならず、マッピングを作成する必要がある。
                    //
                    XmlNamespaceManager nsManager = new XmlNamespaceManager(navigator.NameTable);
                    //
                    // pというプレフィックスをurn:gsf-samples-people.xsdにマッピング。
                    //
                    nsManager.AddNamespace("p", "urn:gsf-samples-people.xsd");

                    //
                    // 各PersonのName要素の値を表示。
                    //
                    // 今回のような場合は、通常使用するように以下のようにしてもデータが取得できない。
                    //foreach(XPathNavigator nav in navigator.Evaluate("//Person/Name/text()") as XPathNodeIterator){
                    //
                    foreach(XPathNavigator nav in navigator.Evaluate("//p:Person/p:Name/text()", nsManager) as XPathNodeIterator){
                        Console.WriteLine(nav.Value);
                    }
                }
            }
        }

        static void Main(){
            new XPathWithNamespaceSample().Execute();
        }
    }
}

出力結果は

gsf_zero1
gsf_zero2
gsf_zero3
gsf_zero4

となります。