Excel VBAで xmlデータを扱うときは名前空間に注意する

サンプルデータと取得方法

サンプルデータと名前空間

下図をサンプルデータとして、データの取得方法を説明する。

図1:サンプルデータ

3行目で名前空間が記述されている。また、6,7行目では名前空間が2つ記述されている。
 ① xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01"
 ② xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd"
①は接頭辞(prefix)が記述されていない。②は「ns2」が記述されている。

このサンプルデータでは、どのダグが①に属し、②に属するのか?
答えは、すべて①の名前空間に属する。「ns2」の接頭辞が付いているタグが無いからだ。

XPathでデータ取得するための"おまじない"

まず、個別のデータ取得処理を記述する前に、おまじない的なコードを書く必要がある。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Dim xml, ns
Set xml = New MSXML2.DOMDocument60

' XMLファイルのロード
If Not xml.Load("C:\Users\user\Desktop\sample.xml") Then
    Debug.Print "XML file load error!"
    Exit Sub
End If
xml.async = False
xml.SetProperty "SelectionLanguage", "XPath"

' プログラム内で使う名前空間の接頭辞aliasを適当に決める(以下は「a」「b」とした)
ns = "xmlns:a='http://mws.amazonservices.com/schema/Products/2011-10-01'"
ns = ns & " " & "xmlns:b='http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd'"
xml.SetProperty "SelectionNamespaces", ns

2行目でxmlオブジェクトを作成して、5行目でサンプルデータが記載されているxmlファイルを読み込んでいる。9行目で同期処理を指定。10行目はXPathを使うためのおまじない。

13~15行目で、プログラム内で使う名前空間の接頭辞を決めている。xmlファイルをロードした時点で名前空間もxmlオブジェクトに入っているのだが、接頭辞を仮にでも決めないとデータ取得できない。この接頭辞の名前は何でもよい。サンプルデータに記載されていた「ns2」に一致させる必要もない。但し、一致させておいた方が無用な混乱を避けられるだろう。

個別のデータ取得方法

◆ 次のコードを実行すると、サンプルデータ4行目の属性statusの値「Success」を取得することができる。

xml.SelectSingleNode("//a:GetLowestOfferListingsForASINResult").getAttribute("status")

SelectSingleNodeの引数にXPathを書いている。ルートから「GetLowestOfferListingsForASINResult」というノード(タグ)を探して、見つかったノードのstatus属性値をgetAttributeで取得している。 また、サンプルデータではこのノードに名前空間の接頭辞は付いていないが、プログラムでは必ず指定する必要がある

◆ 次のコードを実行すると、サンプルデータの11行目のタグ内の値「B002KT3XQM」を取得することができる。考え方は上とほぼ同じだが、タグ内の値を取得する場合はtextを記す。

xml.SelectSingleNode("//a:ASIN").text

◆ サンプルデータの15行目と44行目は同じタグだ。図1ではサンプルデータの末尾まで示していないが、実は繰り返しこのタグは出現している。このタグ内には商品状態を示す タグや、商品の金額、配送料、総額を示すタグが含まれている。これらの値を取得するには、例えば次のように書く。

1
2
3
4
5
6
7
8
9
Dim LowestOfferListing, Amount
Debug.Print "***********"
For Each LowestOfferListing In xml.SelectNodes("//a:LowestOfferListing")
    Debug.Print LowestOfferListing.SelectSingleNode(".//a:ItemCondition").text
    For Each Amount In LowestOfferListing.SelectNodes(".//a:Amount")
        Debug.Print Amount.text
    Next
    Debug.Print "***********"
Next

3行目のSelectNodesで、タグのコレクションを取得し、ループで個々の処理を行っている。 4行目でタグ内の値を取得して出力している。引数のXPathについて、現在のノードを基点に検索するよう「.」が付いていることに注意。 5~7行目でタグの値を取得してる。一つの タグの内部に タグは複数あり(総額・商品金額・配送料)、それぞれをループで取得している。

エラーケース

プログラム内で使う接頭辞を決めなかった場合

次のsample.xmlを読み込み、TitleとURLを出力する処理を検討してみる。

sample.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Sub error1()
    Dim xml, src
    Set xml = New MSXML2.DOMDocument60
    xml.async = False
    xml.SetProperty "SelectionLanguage", "XPath"
'    xml.SetProperty "SelectionNamespaces", "xmlns:a='https://dampgblog.hinohikari291.com/vbaxmlnamespace/'"

    xml.Load ("C:\tmp\sample.xml")
    If xml.parseError.ErrorCode = 0 Then
        Dim article
        Debug.Print "***********"
        For Each article In xml.SelectNodes("//a:article")
            Debug.Print article.SelectSingleNode(".//a:title").Text
            Debug.Print article.SelectSingleNode(".//a:url").Text
            Debug.Print "***********"
        Next
    End If
End Sub

これを実行すると12行目でエラーとなり、図2のメッセージが表示される。

図2:エラー1

ロードしたsample.xmlに記載の接頭辞「a:」をXPath指定しているにも関わらず、エラーとなる。XPathで目的のタグを探すときは、必ずロード前に xmlns:a='https://dampgblog.hinohikari291.com/vbaxmlnamespace/' のように接頭辞付きで名前空間を設定しておく必要がある。
6行目のコメントを外せば、正常に動作する。このコメントを外して実行すると、イミディエイトに以下のログが吐かれる。

名前空間の記述ミス

プログラム内で名前空間の接頭辞を書いてはいるものの、名前空間を示すURLが実際のxmlデータに記載の内容と異なっている場合は、図3のようなエラーとなる。例えば、以下のような 1234567890 などという出鱈目なコードを書いた場合である。

xml.SetProperty "SelectionNamespaces", "xmlns:a='1234567890'"

図3:エラー2

XPathで指定するタグに接頭辞を付けなかった場合

MSXML では接頭辞なしで名前空間を指定する方法がないようなので、適当な接頭辞を決めて XPath を書く必要がある。 図4のように、For Each 文の xml.SelectNodes("//LowestOfferListing")の部分で、接頭辞「a:」を付けていないため、Forループ内に進まないことが分かる。

図4:エラー3

参考サイト

Namespaces in XML 1.0 (Third Edition) W3C Recommendation 8 December 2009
https://www.w3.org/TR/xml-names/#iri-use

名前空間を理解しDOMの概要をつかむ
https://www.atmarkit.co.jp/fxml/rensai2/xmlmaster13/master13.html

関連ページ