VBAでXMLを読み込み解析する場合の実装例とXML操作基礎知識まとめ

VBA
スポンサーリンク

今回の記事では、VBAでXMLデータを読み込んで解析する場合のサンプルプログラムと、読み込んだXMLデータの解析時に必要になる操作方法の知識を紹介していきます。

尚、当ブログでは、以前に「VBAでXMLを作成して出力する場合の実装例とXML操作の基礎知識まとめ」といった記事を公開しましたが、この記事ではあくまでVBAからXMLデータを作成し、それを出力するまでの処理を解説した内容であり、XMLを読み込んで解析する場合は、必要となる技術的な知識が異なります。

上記のリンク先にある記事と今回の記事の内容を組み合わせることで、XMLの読み込み、書き込みのどちらも問題無く習得できた状態になる想定です。
 
 

XMLフォーマットの基礎知識とVBAでXMLを扱う際の設定

「XML」とは、ウェブページなどでも使われる「HTML」にもよく似た書式のデータフォーマットです。
主にアプリケーション間でのデータの受け渡しで使用されます。

HTMLでは文章を”タグ”で囲むことで、そのHTMLページをブラウザが解析し、特定の文字列に装飾をしたり、罫線で囲むなどの表示に変換します。
XMLでも同様にタグでデータを囲みますが、そのタグはブラウザに装飾させるために使われるものではなく、データを階層構造で表現するために使用されます。

一般的に、異なるアプリケーション間でデータを受け渡す場合はCSVファイルなどがよく利用されますが、CSV形式のデータでは、データを単純な表形式でしか表現できません。
XMLではデータを階層構造で表現できるため、複雑なデータ構造を一つのファイルで扱うことが可能です。

詳しくは、前述した以前の記事で紹介しておりますため、興味があれば以下の記事もご一読ください。

VBAでXMLを作成して出力する場合の実装例とXML操作の基礎知識まとめ
今回の記事では、VBAを使って、xml形式のデータを生成してxmlファイルに出力する一連の処理の実装例を紹介します。 ...

このように複雑なデータ構造も表現できることで、却ってXMLの読み込みや書き込みでは処理が複雑になりがちです。

また、XMLをVBAで扱う場合は、VBE側で個別に参照設定を追加しておくと便利です。
参照設定を追加するライブラリ名は、Windows8.1以降のOSでは「Microsoft XML. v6.0」です。

VBAでXMLを扱うために必要な参照設定

参照設定を入れずに、プログラム内で明示的にライブラリを参照してインスタンスを作成する「遅延バインディング」で使用することもできますが、新しいOSと古いOSが混在している環境で使用するケースを除き、参照設定を追加して実装することをおススメします。

当記事でも、参照設定に追加されていることを前提としてサンプルプログラムを紹介します。
 
 

今回の記事で使用するサンプルXML

今回の記事では、以下のXMLファイルを用意して、そのXMLファイルをVBAから読み込みながら実装例を紹介していきます。

  <?xml version="1.0" encoding="utf-8"?>
  <customer>
  	<customer_no>123456</customer_no>
  	<name>
  		<last_name>山田</last_name>
  		<first_name>太郎</first_name>
  	</name>
  	<address>東京都〇〇区なんとかなんとか</address>
  	<telephone>
  		<number type="1">03-1234-5678</number>
  		<number type="2">080-1234-5678</number>
  	</telephone>
  	<email1></email1>
  	<email2/>
  </customer>

簡単なXMLデータですが、冒頭に宣言部分があり、要素で階層化され属性も使用されているため、このXMLから自由に値が取れるようになれば、実務でのXMLの読み込み処理の実装に困ることはないかと思います。

あと、要素の「email1」と「email2」はともに要素の値が空です。
要素の値が無い状態はこのように二種類の表現方法があり、どちらを使用しても問題はありません。
こちらも覚えておいてください。

 
 

VBAでXMLを読み込みする際によく使うDOMオブジェクト

当項では、VBAでXMLファイルを読み込んで解析する場合に、よく使用することになるDOM(Document Object Model)のオブジェクトを抜粋して紹介していきます。

オブジェクト名 オブジェクト型名 解説
Document MSXML2.DOMDocument60 XMLデータ全体を格納するオブジェクト
Node MSXML2.IXMLDOMNode XMLを構成する各データのベースとなるオブジェクト
このオブジェクトと次のリストで大半の処理は賄える
NodeList MSXML2.IXMLDOMNodeList ノードのコレクションを格納するオブジェクト
メソッドやプロパティではこの型が使用されていることが多い
Element MSXML2.IXMLDOMElement 要素を表すオブジェクト
Attribute MSXML2.IXMLDOMAttribute 属性を表すオブジェクト

他にもオブジェクトは色々ありますが、XMLの解析で必ず使用する、又は使用頻度が高いオブジェクトとしてはこれぐらいかと思います。
まずDocumentオブジェクトを生成して、解析するXMLファイルを読み込んだ後、NodeListを作り、ループ処理などでNodeオブジェクトやElementオブジェクトに対してメソッドを実行したり、プロパティを取得するといった操作をするイメージです。

 
 

VBAでXMLを読み込み解析する場合の基本操作

当項では、VBAでXMLファイルの読み込みをして、中身を解析する場合に必要となる、各メソッドやプロパティの基本的な操作方法を、サンプルコードを交えて紹介していきます。

 

要素名を指定してその要素の値を取得する

XML内の特定の要素名を指定してその値を取得したい場合は、DOMDocumentオブジェクトの「getElementsByTagName」を使用します。
この「getElementsByTagName」では、XMLデータの階層に関わらず要素名を指定するだけで要素を取得できるため、予め取得したい要素が決まっている場合は、このメソッドが便利です。

メソッド/プロパティ名 メンバ パラメータ 戻り値
getElementsByTagName MSXML2.DOMDocument60 読み込んだXMLの検索したい要素名
アスタリスク(*)で全要素
指定された要素名のIXMLDOMNodeListオブジェクト

このメソッドでは、DOMDocumentオブジェクト内のXMLを走査し、引数で指定した要素を取得します。
引数に要素名を指定せず、アスタリスクを指定すると、対象のXML内のすべての要素を取得してきます。
戻り値は「IXMLDOMNodeList」オブジェクトです。
戻り値を変数に入れてプロパティやメソッドをVBEで自動表示させたければ、予め戻り値を格納するオブジェクト変数をIXMLDOMNodeList型で宣言しておきます。
 

サンプルコード getElementsByTagName使用例

以下のサンプルコードでは、DOMDocumentオブジェクト内の「getElementsByTagName」を使用して、指定した要素名の要素を取得し、その要素名と要素の値をdebug.printで取り出します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test1()

      Dim DOMDoc As MSXML2.DOMDocument60
      Dim nodeList As IXMLDOMNodeList
      Dim node As IXMLDOMNode
      
          'DOMDocumentのインスタンスを作成します。
          Set DOMDoc = New MSXML2.DOMDocument60

          '解析対象のXMLファイルを読み込みます。
          DOMDoc.Load (XMLFILE)
          
          '要素名を指定して要素リストを取得します。
          Set nodeList = DOMDoc.getElementsByTagName("number")
          '引数にアスタリスクを指定した場合はすべての要素を取得
          'Set elmlist = DOMDoc.getElementsByTagName("*")

          'リストをループします。
          For Each node In nodeList
              '要素の名前を取り出します。
              Debug.Print node.nodeName
              '要素の値を取り出します。
              Debug.Print node.Text
          Next

          Set node = Nothing
          Set nodeList = Nothing
          Set DOMDoc = Nothing

  End Sub

上記の実行結果

number
03-1234-5678
number
080-1234-5678

 

要素名と属性名を指定してその要素内の属性の値を取得する

どの要素にどの属性が予め把握できているケースなどで、要素名と属性名をともに指定してその属性の値を取りたい場合は、IXMLDOMElementオブジェクトの「getAttribute」メソッドを使用できます。
※他にも属性の値を取得する方法はあります。

メソッド/プロパティ名 メンバ パラメータ 戻り値
getAttribute IXMLDOMElement IXMLDOMElement内の要素の検索したい属性名 属性の値(Text)

 

サンプルコード getAttributeメソッド使用例

以下のサンプルコードでは、DOMDocumentオブジェクト内の「getElementsByTagName」を使用して、指定した要素名の要素を取得し、その要素のなかの属性名をgetAttributeメソッドで指定して属性の値をdebug.printで取り出します。
「getAttribute」メソッドはIXMLDOMNodeオブジェクトには存在しないのでご注意ください。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test2()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim nodeList As IXMLDOMNodeList
    Dim elm As IXMLDOMElement
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        '要素名を指定して要素のリストオブジェクトを取得します。
        Set nodeList = DOMDoc.getElementsByTagName("number")

        'リストをループします。
        For Each elm In nodeList
            '属性名を指定して値を取り出します。
            Debug.Print elm.getAttribute("type")
        Next
        
        Set elm = Nothing
        Set nodeList = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

1
2

 

サンプルコード Attributes.getNamedItem(“属性名”)使用例

以下のサンプルコードでは、IXMLDOMNodeオブジェクトから属性名を指定して、属性名と値を取り出します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test2()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim nodeList As IXMLDOMNodeList
    Dim node As IXMLDOMNode
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        '要素名を指定して要素のリストオブジェクトを取得します。
        Set nodeList = DOMDoc.getElementsByTagName("number")

        'リストをループします。
        For Each node In nodeList
            Debug.Print node.Attributes.getNamedItem("type").nodeName
            Debug.Print node.Attributes.getNamedItem("type").NodeValue
        Next
        
        Set node = Nothing
        Set nodeList = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

type
1
type
2

サンプルコード Attributes.Item(index)使用例

以下のサンプルコードでは、IXMLDOMNodeオブジェクトから属性を配列のインデックス番号を指定して、属性名と値を取り出します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test2()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim nodeList As IXMLDOMNodeList
    Dim node As IXMLDOMNode
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        '要素名を指定して要素のリストオブジェクトを取得します。
        Set nodeList = DOMDoc.getElementsByTagName("number")

        'リストをループします。
        For Each node In nodeList
            Debug.Print node.Attributes.Item(0).nodeName
            Debug.Print node.Attributes.Item(0).NodeValue
        Next
        
        Set node = Nothing
        Set nodeList = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

type
1
type
2

 

指定した要素に子要素があるかを取得する

指定した要素に子要素があるかを判別するには、IXMLDOMNodeオブジェクトの「HasChildNodes」メソッドを使用します。
尚、Microsoftのドキュメントでは、ノードに子があるかを判別すると解説されていますが、ここで言う子要素は、階層構造の子ノードだけではなく、対象の要素に値が入っている状態でもTrueになります。
また、対象の要素には子要素が無く、値も無いけど属性が入っている場合はFalseです。

メソッド/プロパティ名 メンバ パラメータ 戻り値
HasChildNodes IXMLDOMNode 真偽※子要素があればTrue

 

サンプルコード HasChildNodesの使用例

以下のサンプルコードでは、すべての要素の子要素有無を判定します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test3()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim elm As IXMLDOMNode
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        'すべての要素を指定してリストオブジェクトを取得します。
        Set elmlist = DOMDoc.getElementsByTagName("*")

        'リストをループします。
        For Each elm In elmlist
            Debug.Print elm.nodeName
            If elm.HasChildNodes Then
            '※参考 以下の書き方でも同じ判定
            'If elm.ChildNodes.Length > 0 Then
                Debug.Print "子ノードあり"
            Else
                Debug.Print "子ノードなし"
            End If
        Next

        Set elm1 = Nothing
        Set elmlist = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

customer
子ノードあり
customer_no
子ノードあり
name
子ノードあり
last_name
子ノードあり
first_name
子ノードあり
address
子ノードあり
telephone
子ノードあり
number
子ノードあり
number
子ノードあり
email1
子ノードなし
email2
子ノードなし

 

指定した要素の数や属性の数を取得する

指定した要素の数を取得する場合は、IXMLDOMNodeListオブジェクトの「length」プロパティから個数を取得できます。
また、要素ごとの属性の数は、IXMLDOMNodeオブジェクト内の要素の「Attributes」プロパティ内の「length」プロパティから取得できます。

メソッド/プロパティ名 メンバ パラメータ 戻り値
lengthプロパティ IXMLDOMNodeList 個数(Long型)

 

サンプルコード Nodeオブジェクトに対するlength取得例

以下のサンプルコードでは、ルート要素(サンプルXMLでは「customer」)配下のすべての要素数を取得します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test4()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim elmlist As IXMLDOMNodeList
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        '要素名を指定して要素のリストオブジェクトを取得します。
        Set elmlist = DOMDoc.getElementsByTagName("*")

        '要素の数を取得します。
        Debug.Print elmlist.Length
        
        Set elmlist = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

11

サンプルコード NodeオブジェクトのAttributesに対するlength取得例

以下のサンプルコードでは、要素名を指定して、その要素ごとに含まれる属性の個数を取得します。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test4()

    Dim DOMDoc As MSXML2.DOMDocument60
    Dim elmlist As IXMLDOMNodeList
    Dim elm As IXMLDOMNode
    
        'DOMDocumentのインスタンスを作成します。
        Set DOMDoc = New MSXML2.DOMDocument60

        '解析対象のXMLファイルを読み込みます。
        DOMDoc.Load (XMLFILE)
        
        '要素名を指定して要素のリストオブジェクトを取得します。
        Set elmlist = DOMDoc.getElementsByTagName("number")

        'リストをループします。
        For Each elm In elmlist
            '属性の数を取得します。
            Debug.Print elm.Attributes.Length
        Next
        
        Set elmlist = Nothing
        Set DOMDoc = Nothing

  End Sub

上記の実行結果

1
1

 

階層ごとにループを入れ子して要素の名前と値を取得する

ループを入れ子しながら、浅い階層から深い階層へ順に要素の名前と値を取得する場合は、IXMLDOMNodeオブジェクトの「ChildNodes」プロパティを使用します。
このプロパティでは子ノードのコレクションを保持します。
そのコレクションからIXMLDOMNodeオブジェクトを生成して、更に「ChildNodes」プロパティを参照していくことで、階層ごとに要素を取ることが可能です。
※他にも色々方法はあります。

メソッド/プロパティ名 メンバ パラメータ 戻り値
childNodesプロパティ IXMLDOMNode childNodesプロパティ (コレクション)

 

サンプルコード ChildNodes使用例

下記のサンプルコードでは、浅い階層から順にループを入れ子して要素の名前と値を取得しています。
ループの状態によっては、コレクション内に「Text」が無いのに値を取ろうとしている結果、debug.printの出力ではエラーになっていますが、一応動きます。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test5()

      Dim DOMDoc As MSXML2.DOMDocument60
      Dim elmlist As IXMLDOMNodeList
      Dim elm As IXMLDOMNode
      Dim elm1 As IXMLDOMNode
      Dim elm2 As IXMLDOMNode
      
          'DOMDocumentのインスタンスを作成します。
          Set DOMDoc = New MSXML2.DOMDocument60

          '解析対象のXMLファイルを読み込みます。
          DOMDoc.Load (XMLFILE)
          
          Set elmlist = DOMDoc.ChildNodes
          
          For Each elm In elmlist
              
              Debug.Print elm.nodeName
              
              For Each elm1 In elm.ChildNodes
              
                  Debug.Print elm1.nodeName
                  
                  For Each elm2 In elm1.ChildNodes
                      
                      Debug.Print elm2.nodeName
                      Debug.Print elm2.Text
                      
                  Next
              Next
          Next
          
          Set elm2 = Nothing
          Set elm1 = Nothing
          Set elm = Nothing
          Set elmlist = Nothing
          Set DOMDoc = Nothing

  End Sub

上記の実行結果

xml
customer
customer_no
#text
123456
name
last_name
山田
first_name
太郎
address
#text
東京都〇〇区なんとかなんとか
telephone
number
03-1234-5678
number
080-1234-5678
email1
email2

 

再帰ですべての要素とその値を取得する

XMLフォーマットは階層構造でデータを表現することができます。
階層構造が予め固定されているデータであれば、条件分岐をしながらデータを解析して要素ごとの値を取り出すことができますが、階層が深い場合や、階層構造が予め固定されていないデータを網羅的に読み込むためには、再帰関数を作成し、再帰で動的に読み込みをします。

再帰でXMLを解析する場合はIXMLDOMNodeオブジェクトの「HasChildNodes」メソッドでその要素が空か否かを判定しつつ、IXMLDOMNodeオブジェクトの「ChildNodes」プロパティを再帰関数に再帰的に渡します。
また、ノードのコレクション内の種類を判定するために、IXMLDOMNodeオブジェクトの「NodeType」プロパティも取得します。

メソッド/プロパティ名 メンバ パラメータ 戻り値
HasChildNodes IXMLDOMNode 真偽※子がある場合はTrue
nodeTypeプロパティ IXMLDOMNode ノードの種類(数値)

尚、nodeTypeプロパティで保持しているノードの種類は、以下のリンク先でご確認ください。

XML DOM Enumerated Constants

 

サンプルコード 再帰実装例

以下のサンプルコードでは、すべての要素名とその値を再帰で取得します。
このサンプルでは要素の名前と値のみ取得していますが、更に属性の有無の判定や、属性の個数も取得することで、属性の名前や値も含めて再帰で取得可能です。

  Option Explicit

  Const XMLFILE As String = "C:\sample.xml"

  Sub test6()

      Dim DOMDoc As MSXML2.DOMDocument60
      
          'DOMDocumentのインスタンスを作成します。
          Set DOMDoc = New MSXML2.DOMDocument60

          '解析対象のXMLファイルを読み込みます。
          DOMDoc.Load (XMLFILE)
          
          Call xmlReAnalysis(DOMDoc.ChildNodes)

          Set DOMDoc = Nothing

  End Sub


  'XML解析用再帰関数
  Sub xmlReAnalysis(objNode As IXMLDOMNodeList)

      Dim elm As IXMLDOMNode

      For Each elm In objNode
          'ノードの種類で処理を分岐します。
          Select Case elm.NodeType
              '要素の場合
              Case 1
                  Debug.Print elm.nodeName
              'テキスト(値)の場合
              Case 3
                  Debug.Print elm.Text
          End Select
          '対象の要素に子があるか判定します。
          If elm.HasChildNodes Then
              '子があれば再帰関数を呼びます。
              Call xmlReAnalysis(elm.ChildNodes)
          End If
      Next
      
      Set elm = Nothing

  End Sub

上記の実行結果

customer
customer_no
123456
name
last_name
山田
first_name
太郎
address
東京都〇〇区なんとかなんとか
telephone
number
03-1234-5678
number
080-1234-5678
email1
email2

 
 

最後に

今回の記事では、VBAでXMLを読み込んで解析する場合の必要になる知識と実際のサンプルコードを紹介しました。

最近のデータ交換用フォーマットでは、JSON形式が採用されることも多いのですが、XML形式もまだまだ頻繁に目にします。
VBAでXMLを扱えるようにしておくと、VBAを利用した他システム連携なども可能になるため、そのようなケースで非常に重宝します。

慣れるまでは難しく感じますが、是非挑戦してみてください。

今回も長々と読んでいただきましてありがとうございます。
それでは皆さん、ごきげんよう!

タイトルとURLをコピーしました