ホーム  > X-plus >  XML解説

この記事を印刷する この記事を送る はてなブックマークに追加する
テキストリンクコードを取得する

Developer's Cafe XMLをおいしく味わおう!~Episode 15 : .NETでXMLを扱う(2)

2005年02月01日作成 

吉田 晃伸
今回は.NETを使用してXMLを扱うシリーズの第2回目となります。DOMの基本部分はJavaとほとんど変わらないので割愛しますが、今回からは.NETで独自実装されている便利なメソッドの使い方などを中心にご紹介していきます。より実践的なプログラミングを行っていきましょう!

本稿執筆時点ではまだ冬真っ最中。今シーズンの最初は雪不足が懸念されましたが、終わってみれば(まだシーズンは終わってないけど)例年並みに降った良いシーズンでしたね1。 皆さんはスキーシーズンを十分に楽しまれたでしょうか?私ですか?私は今シーズンは本業がなかなか忙しく全然スキーにいけませんでしたが、、、来年は久しぶりに海外にでも行ってみたいものです2

そんなこんなで消化不良の冬が終わり、まもなく春です。春は消化不良にならないように、いっぱい取材に出かけたいと考えています(^^)vご理解とご協力のほどよろしくお願いします。

さて、早速本題に入りましょう。今回は、.NETを使ったXMLの操作の第2回目です。先回も紹介しましたが、DOMプログラミングをするうえでは、W3Cが標準化していますので、Javaだろうと.NETだろうと変わりません。

。。。としてしまうと、.NETで書く内容がなくなってしまうのですが(^_^;) 実は.NETでは、DOMを独自拡張させたメソッドやプロパティが実装されています。今回から2回に渡ってそうした独自実装の内容などを紹介していきたいと思います。

特定の要素の内容を取得する

XMLデータの操作としてよくあるのは、ある特定の要素の内容を取得するというものではないでしょうか?まず、通常のDOMを使った方法を考えてみましょう。リスト1のようなサンプルXMLデータがあったとします。これは、自動車保険の内容を表したサンプル(のつもり(*^_^*))です。これを例に考えて見ましょう。

リスト1:サンプルXML

<?xml version="1.0" encoding="UTF-8"?>
<autoinsurance>
 <!--自動車保険1人分の契約を表す-->
 <contractbasic>
 <!--契約の基礎情報。支店情報など-->
  <type>One Do</type>
  <number>EEEEE-98989898</number>
  <branch>横浜支店</branch>
  <PREPARED>山田太郎</PREPARED>
 </contractbasic>
 <personalinfo>
  <!--個人情報-->
  <name>テスト太郎</name>
  <zip>111-1111</zip>
  <address>虎ノ門</address>
  <telnum>03-000-00000</telnum>
  <birthday>昭和20年</birthday>
  <sex>男</sex>
  <license>ゴールド</license>
 </personalinfo>
 <autoinfo>
  <!--車種情報-->
  <name>フェラーリF40</name>
  <displacement>3000CC</displacement>
  <carlicense>横浜 330 い 7890</carlicense>
  <grantday>2001年11月23日</grantday>
  <inspectionday>2004年11月</ inspectionday>
  <purpose>個人</purpose>
 </autoinfo>
 <coverage>
  <!--詳細内容-->
  <propertydamagerank>対物</  propertydamagerank>
  <personaldamagerank>対人</personaldamagerank>
  <cardamagerank>車両</cardamagerank>
  <propertydamage>1</propertydamage>
  <personaldamage>1</personaldamage>
  <cardamage>1</cardamage>
 </coverage>
 <cost>80000</cost>
</autoinsurance>

このXMLの中から契約されている保険の自動車名を取得したいとします。自動車情報はautoinfo要素に格納されています。そして、自動車の名称はname要素のようです。リスト2にDOMで作成したサンプルを示します。DOMのElementクラスのメソッドに「GetElementsByTagName」が用意されています。これは、特定の要素名を持つノードを取得してくるメソッドです。これを使用してプログラムを組むとどんな感じになるでしょうか?リスト2では、最上位要素のElementオブジェクトに対して「GetElementsByTagName」を実行してみました。また、図2に、その結果を示します。

リスト2

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim doc As New XmlDocument()
        Dim docElement As XmlElement
        Dim xmlStr As String


        doc.Load("C:¥WORK¥sample¥aut.xml")
        MessageBox.Show("XML文書を読み込みました。", "状態")
        docElement = doc.DocumentElement
        xmlStr = docElement.Name
        Dim str As String

        str = getName(docElement)

    End Sub

    Function getName(ByVal elm As XmlElement)
        Dim node As XmlNode
        Dim child As XmlNode
        Dim nodelist As XmlNodeList
        Dim value As String

        '指定した要素名のNodeを取得
        nodelist = elm.GetElementsByTagName("name")
        For Each node In nodelist
            child = node.FirstChild()
            MessageBox.Show(child.Value, "Name要素の内容")
        Next
    End Function

、、、っと!自動車名も取得できましたが、「テスト太郎」という考えていなかったデータまで取得できています。これはなんで?と思ってリスト1のXMLデータをよ~~く眺めてみてください。ありました「personalinfo」という契約者の個人情報を表すデータブロックにも「name」要素が存在しているからなんですね。。。これではこのプログラムは本来の目的を果たしているとはいえませんね、、(T_T)

では、いったいなぜこのような動きになるのでしょうか?今回のプログラムにおける「GetElementsByTagName」の動きを図に表してみました(図3)。

この図からわかるように、「GetElementsByTagName」は、メソッドが実行されたElementオブジェクトの配下に存在するすべての「name」要素を取得してくるのです。今回のサンプルにたまたま同じ要素名が存在したため、このようになったのです。

これを正しいプログラムにするにはどうすればいいでしょうか?やり方はいくつかありますが、リスト3のようにして対応することが可能です。

リスト3:正しいプログラム

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim doc As New XmlDocument()
        Dim docElement As XmlElement
        Dim xmlStr As String


        doc.Load("C:¥WORK¥sample¥aut.xml")
        MessageBox.Show("XML文書を読み込みました。", "状態")
        docElement = doc.DocumentElement
        xmlStr = docElement.Name
        Dim str As String
        Dim autoinfo As XmlNode
        Dim elmautoinfo As XmlElement

        autoinfo = getAutoinfo(docElement)
        elmautoinfo = CType(autoinfo, XmlElement)
        autoinfo = getName(elmautoinfo)

        'str = getAutoName(docElement)

    End Sub

    Function getAutoinfo(ByVal elm As XmlElement)
        Dim node As XmlNode
        Dim child As XmlNode
        Dim nodelist As XmlNodeList
        Dim value As String

        '指定した要素名のNodeを取得
        nodelist = elm.GetElementsByTagName("autoinfo")
        'サンプルのXMLにautoinfo要素は1つしか存在しないため
        node = nodelist.Item(0)
        getAutoinfo = node

    End Function

    Function getName(ByVal elm As XmlElement)
        Dim node As XmlNode
        Dim child As XmlNode
        Dim nodelist As XmlNodeList
        Dim value As String

        '指定した要素名のNodeを取得
        nodelist = elm.GetElementsByTagName("name")
        For Each node In nodelist
            child = node.FirstChild()
            MessageBox.Show(child.Value, "Name要素の内容")
        Next
    End Function

ここでは、新しく「getAutoinfo」メソッドを作成しました。これはXMLデータ中に存在する「autoinfo」要素を取得するものです。そして、取得できた「autoinfo」要素のElementオブジェクトに対して「GetElementsByTagName」を実行することにより正しい結果を得ることができるようになりました(図4)。

このように「GetElementsByTagName」を実行する対象となる要素をプログラムの前段階の処理として絞り込むことにより、正しいデータを取得できるようになるのです。しかし、、、もう少し簡単にできないものでしょうか?今回のサンプルではたまたま「autoinfo」要素が1つでしたし、name要素の使用箇所も少なかったのです。

こうした方法をいくらか簡単にする便利なメソッドが用意されています(.NETの独自拡張)。それがElementクラスに用意されている「SelectNodes」メソッドです。この使い方を早速見てみましょう。リスト4にそのサンプルプログラムを示します。「SelectNodes」メソッドの引数にご注目ください。なんだかフォルダー構成を表すような書き方になっているのがお分かりでしょう。これは、XPathという言語を使用して、XMLの内容を指し示しているのです。

リスト4

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim doc As New XmlDocument()
        Dim docElement As XmlElement
        Dim xmlStr As String


        doc.Load("C:¥WORK¥sample¥aut.xml")
        MessageBox.Show("XML文書を読み込みました。", "状態")
        docElement = doc.DocumentElement
        xmlStr = docElement.Name
        Dim str As String

        str = getAutoName(docElement)

    End Sub

    Function getAutoName(ByVal elm As XmlElement)
        Dim node As XmlNode
        Dim child As XmlNode
        Dim nodelist As XmlNodeList
        Dim value As String

        '指定した要素名のNodeを取得
        nodelist = elm.SelectNodes("/autoinsurance/autoinfo/name")
        For Each node In nodelist
            child = node.FirstChild()
            MessageBox.Show(child.Value, "車の名前")
        Next
    End Function

XPath、聞きなれない方もいらっしゃるでしょうか?これはXSLTなどにも使用されているXMLの階層構造やその位置を示すのに使用される言語でW3Cが標準化しているものです。「SelectNodes」メソッドでは、このXPathを使用して、単純に要素名を指定するのではなく、「XXX要素の下のName要素」といった指定が簡単にできるようになります。このXPathの基本的な書き方ですが、XML構造の階層構造を「/」を使用して表します。今回の引数に指定したXPathを見てみると、図5のようになっています。

このようにXPathにより、構造を絞って要素名を特定できるようになっているのです。これなら、Nmae要素がいくつあっても、上位からの構造をしっかりと記述できれば正しい内容を取得できることでしょう。このリスト4の実行結果を図6に示します。正しく自動車の名称のみが取得できたことがわかるでしょう!

まとめ

今回は.NETで扱うXMLの第二回目として、DOMの標準には用意されていない便利なメソッドに関してご紹介しました。実はこれに似たメソッドはDOMのレベル3で策定が進んでいます。Microsoftではだいぶ前から実装されていましたので、ようやく標準が追いついてきたというところでしょうか。また次回も、.NETでどのようにXMLを扱えるのかを引き続き取り上げていきます。






ページトップへ戻る