VBAにてXMLの値を読込むには?

解決


超特急  2006-05-26 01:47:10  No: 95508

OS:WinXP Pro/Access:2003
XMLに記述されている各タグの属性値(Attribute)を読込む処理を
Access2003のVBAにて作成することとなりました。
XMLには原則としてタグに対しては属性値しか存在しません。
以下、使用するXMLのイメージ
<?xml version="1.0" encoding="Shift_JIS" ?>
<タグ1 属性11="m" 属性12="100" 属性13="xxxxxxxxxx"/>
    <タグ2 属性21="abc" 属性22="w"?>
    <タグ3>
        <タグ4 属性41="1" 属性42="AAA" 属性43="c">
            <タグ5 属性51="abc" 属性52="1" 属性53="x"/>
            <タグ5 属性51="def" 属性52="2" 属性53="xx""/>
            <タグ5 属性51="ghi" 属性52="3" 属性53="xxx"/>
            <タグ5 属性51="jkl" 属性52="4" 属性53="xxxx"/>
            <タグ5 属性51="mno" 属性52="5" 属性53="xxxxx"/>
        </タグ4>
        <タグ4 属性41="2" 属性42="BBB" 属性43="D">
            <タグ5 属性54="1000" 属性55="a" 属性56="x"/>
            <タグ5 属性54="1100" 属性55="b" 属性56="xx""/>
            <タグ5 属性54="1200" 属性55="c" 属性56="xxx"/>
            <タグ5 属性54="1300" 属性55="d" 属性56="xxxx"/>
            <タグ5 属性54="1400" 属性55="e" 属性56="xxxxx"/>
        </タグ4>
    </タグ3>
<タグ2>
Access2003のVBAにて、上記XMLの各タグの属性値を
上から順番に読取る処理を実現するための最適なコーディングを
ご教授いただけないでしょうか。
またXML処理を行うために必要なコンポーネント等あれば、
併せてご教授願います。
客先環境のため資料等、情報が不足していまして、
藁をもすがる思いでのお願いです。
以上、宜しくお願いいたします。


魔界の仮面弁士  2006-05-26 03:54:19  No: 95509

> XMLには原則としてタグに対しては属性値しか存在しません。
要素ノード内のテキスト(改行やタブ)は無視したい…という事でしょうか。

> 以下、使用するXMLのイメージ
……単なる書き間違いだと思いますが、XML の構文になっていませんね。(^^;

> 最適なコーディング
何が「最適」かは、人それぞれなので、これについては触れずにおきますが、

> またXML処理を行うために必要なコンポーネント等あれば、
こちらについては、MSXML を使うのが手軽でよいでしょう。

参照設定に "Microsoft XML, v?.?" を加えた上で(4.0以上を推奨)、

Dim D As MSXML2.DOMDocument
Set D = New MSXML2.DOMDocument
D.async = False '同期読み込み
If D.Load("C:\SAMPLE.XML") Then
    MsgBox "読み込み成功"
Else
    MsgBox "読み込み失敗"
    Debug.Print D.parseError.errorCode
    Debug.Print D.parseError.reason
    Debug.Print D.parseError.srcText
    Debug.Print D.parseError.Line
    Debug.Print D.parseError.linepos
End If

のように書きます。ファイルからではなく、文字列から読み込む場合は、
  If D.Load("C:\SAMPLE.XML") Then
の部分を、
  If D.LoadXML(strXMLText) Then
のように変更してください。

XMLの読み込みができれば、あとは下記のような感じです。

  'ルート要素ノードの名前
  Debug.Print D.documentElement.nodeName
  'ルート要素ノードが持つ属性の数
  Debug.Print D.documentElement.attributes.length
  'ルート要素ノードの0番目の属性名
  Debug.Print D.documentElement.attributes(0).nodeName
  'ルート要素ノードの0番目の属性値
  Debug.Print D.documentElement.attributes(0).nodeValue
  'ルート要素ノードの最初の子ノードの"属性21"属性の値
  Debug.Print D.documentElement.firstChild.attributes.getNamedItem("属性21").nodeValue

また、事前に
  D.setProperty "SelectionLanguage", "XPath"
のように設定しておけば、XPath による検索もできます。たとえば、
  <タグ1>
    <タグ3>
      <タグ4 属性41="1"><タグ5 属性51="abc" 属性52="x" /></タグ4>
      <タグ4 属性41="2"><タグ5 属性54="def" 属性53="y" /></タグ4>
    </タグ3>
  </タグ1>
という XML に対して
  Debug.Print D.selectSingleNode("/タグ1/タグ3/タグ4[@属性41='2']/タグ5/@属性54").nodeValue
とすると、"def" という結果が得られますし、
  For Each node In D.selectNodes("//タグ5/@*")
    Debug.Print node.nodeName, node.nodeValue
  Next
などとすれば、
  属性51  abc
  属性52  x
  属性54  def
  属性53  y
という結果が得られる事になります。


VB  2006-05-26 03:55:13  No: 95510

MSXML2.DOMDocumentを使用して

Dim xObj As MSXML2.DOMDocument

Set xObj = New MSXML2.DOMDocument
    
xObj.Load ("読み込むXMLファイル")

でどうでしょう?
私も最近やったのですが。

これでとりあえずはXML読めます。


超特急  2006-05-26 05:51:26  No: 95511

魔界の仮面弁士様、VB様、ご教授ありがとうございます。
魔界の仮面弁士様より教えていただいた方法で、各タグの
属性値を読取ることはできました。
今回、私が詰まっている箇所は、対象となるXMLの内容が変化することです。
タグの数量、階層、属性の数等は編集者の任意によって変化します。
処理としては構成に関わらず、対象XMLファイル内に記述されている
全てのタグの全ての属性値を読込むことです。
その為、以下の条件が求められます。
・先頭タグを読込後、子となるタグの存在をチェックする。
・子タグが存在する場合は、子のタグの属性値を全て読込む。
・親、子ともにタグが存在しても属性が無い場合は読み飛ばし次のタグを探す。
上手く次のタグを検索しながら、階層を下って行くにはどうすればよろしいでしょうか?
重ね重ね、申し訳ございませんが再度、ご教授いただけますでしょうか。
どうか宜しくお願いいたします。


魔界の仮面弁士  2006-05-26 08:40:03  No: 95512

> 今回、私が詰まっている箇所は、
こちらをどうぞ。MSXML4 パーサのヘルプがあります。
http://www.microsoft.com/downloads/details.aspx?FamilyID=3144b72b-b4f2-46da-b4b6-c5d7485f2b42&DisplayLang=ja

また、参照設定した MSXML コンポーネントのメンバ一覧を、
Access の「オブジェクト ブラウザ」で眺めてみたりしても、
ある程度、使い方がわかるのではないかと思います。

> タグの数量、階層、属性の数等は編集者の任意によって変化します。
今回のように、属性を列挙するだけのような状況では、先に示した
「DOM」による読取よりも、「SAX」の方が効率が良いかも知れません。
DOM は、XML 全体をロードしますが、SAX では順に読み取っていくため、
メモリ効率もよく、大きなファイルも効率よく取り出せます。
(幸い、MSXML は DOM と SAX の両方をサポートしています)

ただ、先に示した DOM でも処理する事はできます。
たとえば、子要素ノードを列挙するなら、childNodes プロパティを
For なり For Each なりで回してやるだけで OK です。
(孫要素まで取り出すなら、それを再帰処理するだけですね)

また、すべての属性値を取り出すにしても、先に示したサンプルのように、
attributes プロパティを列挙すれば OK。これは説明不要ですよね。
>  Debug.Print D.documentElement.attributes.length
>  Debug.Print D.documentElement.attributes(0).nodeName
>  Debug.Print D.documentElement.attributes(0).nodeValue

あるいは、一括してXPath で検索して取り出すのも良いでしょう。
  For Each attributeNode In .selectNodes("//@*")
    Debug.Print attributeNode.nodeName,
    Debug.Print attributeNode.nodeValue
  Next

他にも、「DOM」や「SAX」による列挙ではなく、「XSLT」を使って、
XML をタブ区切りテキストなどに変換する…などという手もあります。
(これも、MSXML で処理する事が可能です)

——などと、XML を読み取る方法を幾つか紹介しましたが、結局のところ、
何を持ってして
> 最適なコーディング
とみなすかは、超特急さんが決める事ですね。

MSXML 以外にも、XML用のパーサは存在しますので、それらについても
追加調査した上で、要件にあうと思われる方法を選択してみてください。


超特急  2006-05-26 18:29:59  No: 95513

魔界の仮面弁士様、ご丁寧に教えてくださって、
本当にありがとうございます。
教えていただいた手法、情報を基にこれからがんばってみます!!
また不明点が出てきた際は、ここで質問させていただきます。
本当に、本当にありがとうございます!!
良い結果が報告できるように努力します。


超特急  2006-05-31 01:41:16  No: 95514

魔界の仮面弁士様をはじめ、皆様ありがとうございました。
何とか自分の希望する動作が実現できました。
本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加