XPathの動作確認(Excel VBA編)

サンプルドキュメント

図1のXMLドキュメントファイルをVBAプログラムでロードして、XPathのテストを行う。 構造としては、Studentsの子要素にStudentが複数あり、さらにその子要素にIdとsubject別のScore要素がある。生徒・科目別の試験点数をイメージして作成したデータだ。

図1.テストデータ(XMLドキュメント

XPathの実装例

フルパス指定

' フルパス指定
Debug.Print doc.SelectSingleNode("/Students/Student/Id").text ' 20200025

上記コードを実行すると、イミディエイトウィンドウに「20200025」が出力される。コードの右端に出力値を載せている。Students の子要素に Student は複数あるが、特に指定がなければ最初の1つ目の要素が取得される。

@で属性を指定

' 属性値を指定
Debug.Print doc.SelectSingleNode("/Students/Student/Score[@subject='Science Math']").text ' 53
' 「//」でルートから検索1
Debug.Print doc.SelectSingleNode("//Score[@subject='Science Math']").text ' 53
' 「//」でルートから検索2 「*」ですべてのタグが対象
Debug.Print doc.SelectSingleNode("//*[@subject='Science Math']").text     ' 53

subject属性が「Science Math」と一致する要素がヒットする。

containsの使い方

' containsの使い方1
Dim wkNode
For Each wkNode In doc.SelectNodes("//Score[contains(@subject, 'Politics')]")
    Debug.Print wkNode.text ' 44, 92, 83
Next
' containsの使い方2
Debug.Print doc.SelectSingleNode("//Id[contains(text(),'202003')]").text ' 20200359

1では属性に対してcontainsを適用、2ではテキストノードに対して適用している。 1では、subject属性に「Politics」という文字列が含まれるScore要素がヒットする。2では、テキストノードに「202003」という文字列が含まれるID要素がヒットする。

position で要素の位置を指定

' 要素の位置を指定1
Debug.Print doc.SelectSingleNode("//Score[contains(@subject, 'Lang')][position()=2]").text ' 56
' 要素の位置を指定2
Debug.Print doc.SelectSingleNode("//Score[contains(@subject, 'Lang')][2]").text            ' 56
' 要素の位置を指定3
For Each wkNode In doc.SelectNodes("//Student[3]/Score[position()>=2][position()<3]")
    Debug.Print wkNode.text ' 30, 33
Next

1では、まずsubject属性値に文字列「Lang」が含まれる要素がヒットする。テストデータを見ればわかるが、6個の要素がヒットする。この6個のうち、positionで指定された2番目が最終的にヒットする。 2は1の省略形。 3では、まず3番目のStudent要素の子要素Scoreのうち、2番目以上の要素 (図の赤色枠) がヒットする。その赤枠内での3番目未満 の要素 (図の青色枠) が最終的にヒットする。

図2.positionを使って要素を特定する

sum, countの使い方

Debug.Print doc.SelectSingleNode("//Student[sum(Score/text()) div count(Score) > 80]/Id").text ' 20200186

当記事のテストデータは、生徒・科目別のテスト点数をイメージして作成したものだが、上記コードでは、平均点が80点以上の生徒のIDが取得される。

まず、sum(Score/text())について。Studentの子要素にはsubject別に5個のScore要素があり、さらに各Score要素のテキストノードに得点が表記されている。このテキストノードの値を全て集計している。 次に、count(Score)では、Studentの子要素Scoreの数をカウントしている。 div は除算を示す演算子だ。 結局、合計点数を科目数で割り算して平均値を算出し、計算結果が80以上のStudentを特定している。さらに、その特定されたStudentの子要素Idを取得している。

and, or の使い方

' and の使い方1
Debug.Print doc.SelectSingleNode("//Student[Score[contains(@subject,'Physics') and text() >= 80]]/Id").text ' 20200186
' and の使い方2
For Each wkNode In doc.SelectNodes("//Student[2]/Score[position()>=3 and position()<5]")
    Debug.Print wkNode.text ' 95, 87
Next
' or の使い方
Debug.Print doc.SelectNodes("//Id[contains(text(),'202001') or contains(text(),'202002')]")(0).text        ' 20200186

「and の使い方1」では、物理(Physics)の点数が80点以上の生徒のIDが取得される。 「and の使い方2」では、2番目のStudent要素の子要素Scoreのうち、3番目以上5番目未満の要素が取得される。上で説明済みのpositionのコード例と比べてほしいが、Score[position()>=3 and position()<5]Score[position()>=3][position()<5]では意味が違うことに注意。

following-sibling の使い方

Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/following-sibling::Score").text    ' 54
Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/following-sibling::Score[1]").text ' 54
Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/following-sibling::Score[2]").text ' 83

following-sibling は、基点より後にある兄弟要素を検索する。

//Student[Id='20200359']/Score[3]でヒットした要素を基点として、その後方の兄弟Score要素が検索される。なお、自分自身は含まれない。

following-sibling::Scoreの末尾に position 指定がない場合は、ヒットした要素グループの先頭要素が取得される。 position 指定がある場合は、基点から後方に向かって position 番目の要素が取得される。

preceding-sibling の使い方

Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/preceding-sibling::Score").text    ' 80
Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/preceding-sibling::Score[1]").text ' 30
Debug.Print doc.SelectSingleNode("//Student[Id='20200359']/Score[3]/preceding-sibling::Score[2]").text ' 80

preceding-siblingは、基点より前にある兄弟要素を検索する。

//Student[Id='20200359']/Score[3]でヒットした要素を基点として、その前方の兄弟Score要素が検索される。なお、自分自身は含まれない。

preceding-sibling::Scoreの末尾に position 指定がない場合は、ヒットした要素グループの先頭要素が取得される。 position 指定がある場合は、基点から前方に向かって position 番目の要素が取得される。

参考サイト

XPathについて初心者の場合は、まず以下サイトでイメージをつかむことをお勧めする。
https://www.techscore.com/tech/XML/XPath/index.html/
https://qiita.com/rllllho/items/cb1187cec0fb17fc650a

関連ページ