XPathを用いたデータの操作方法については、以前 http://d.hatena.ne.jp/gsf_zero1/20070117/p2 や http://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
となります。