XMLファイルのタグの属性を読み取るには?

解決


初心者  2004-09-16 02:16:30  No: 116315

'####################  XMLファイルインプット用  ####################

Public Sub LoadXMLDocument()
Dim xDoc As MSXML.DOMDocument
Set xDoc = New MSXML.DOMDocument
xDoc.validateOnParse = False
If xDoc.Load("C:\Document\testXML.xml") Then     '
    DisplayXMLNode xDoc.childNodes, 0
Else
    ' エラー情報
    Dim strErrText As String
    Dim xPE As MSXML.IXMLDOMParseError
   ' ParseError オブジェクトを取得
    Set xPE = xDoc.parseError
    With xPE
    strErrText = "次の XML ドキュメントの読み込みに失敗しました :" & _
        "次のエラーが原因です :" & vbCrLf & _
        "エラー # : " & .errorCode & ": " & xPE.reason & _
        "行 #: " & .Line & vbCrLf & _
        "行位置 : " & .linepos & vbCrLf & _
        "ファイル内の位置 : " & .filepos & vbCrLf & _
        "ソース テキスト : " & .srcText & vbCrLf & _
        "ドキュメント URL : " & .URL
    End With

    MsgBox strErrText, vbExclamation
    
End If

Set xPE = Nothing

End Sub

Public Sub DisplayXMLNode(ByRef Nodes As MSXML.IXMLDOMNodeList, _
    Indent As Integer)

    Dim xNode As MSXML.IXMLDOMNode
    Indent = Indent + 2
    
    For Each xNode In Nodes
        If xNode.nodeType = NODE_TEXT Then
            Debug.Print Space$(Indent) & xNode.parentNode.nodeName & _
            ":" & xNode.nodeValue
        ElseIf xNode.nodeType = NODE_ELEMENT Then
→→→→→→            
        End If
        
        If xNode.hasChildNodes Then
            DisplayXMLNode xNode.childNodes, Indent
        End If
    Next xNode
End Sub

矢印(→)部をどう処理していいのかよくわかりません。
(下記XML文書の場合の"currency=dollar"のdollarを取り込みたい)

<cars>
  <car>
    <name>乗用車</name>
    <price currency=dollar>150</price>
  </car>
  <car>
    <name>トラック</name>
    <price currency=dollar>500</price>
  </car>
  <car>
    <name>オープンカー</name>
    <price currency=dollar>200</price>
  </car>
</cars>

初心者のため、説明等不足が多いと思いますが
ご指導願います。

参考になるホームページや本でもよいので
教えてください。

よろしくお願いします。


初心者  2004-09-16 02:17:58  No: 116316

開発環境を書くのを忘れていました。
「VB6」です。
よろしくお願いします。


魔界の仮面弁士  2004-09-16 10:06:54  No: 116317

# できれば MSXML1 ではなく、MSXML4 以降を使った方が良いかと。

> (下記XML文書の場合の"currency=dollar"のdollarを取り込みたい)
……その前に、提示されたデータは、XMLの文法になっていませんよね。
読み込み時に、構文エラーになりませんでしたか? (^_^;)

まぁ、XMLが正しいという前提であれば、値を読み書きするために
標準の『nodeValueプロパティ』を利用する事ができます。あるいは、
Microsoft独自プロパティの「textプロパティ」を使う事もできるでしょう。

Dim N As IXMLDOMNode
For Each N In xDoc.selectNodes("//*|//@*")
    Debug.Print N.nodeTypeString; ","; N.nodeName; ","; N.nodeValue
Next

ただ、NODE_ELEMENT は「要素」になってしまいます。
「属性」なら、NODE_ATTRIBUTE となりますね。

もし、要素が持っている属性を取得したいのであれば、
  ・xNode.attributes
  ・xNode.getNamedItem
  ・xNode.selectNodes
  ・xNode.selectSingleNode
などを利用できます。SDKで確認してみてください。


魔界の仮面弁士  2004-09-16 10:20:08  No: 116318

おっと、テキストノードも取得するんでしたね。

誤) For Each N In xDoc.selectNodes("//*|//@*")
正) For Each N In xDoc.selectNodes("//*|//@*|//text()")

# コメントノードや処理命令ノードは無視して良いのかな?

元の DisplayXMLNode() にて処理するなら、たとえば、
  Dim Attr As IXMLDOMAttribute
のような変数を宣言しておき、先の「→→→→→→」部に、
  For Each Attr In xNode.Attributes
    Debug.Print Space$(Indent) & "@" & Attr.nodeName & ":" & Attr.nodeValue
  Next
などと書いても処理できるかと。


初心者  2004-09-16 18:31:55  No: 116319

魔界の仮面弁士  様

ありがとうございます。
なんだか、感動してしまいました。
(と共に自分がいかにVBも何もわかっていないか痛感しました)

まずXMLですが、急いで書いてしまったので、
すみません。
○→<price currency='dollar'>150</price>
です。

魔界の仮面弁士さんのアドバイスに従って
下記のように直してみました。

Public Sub DisplayXMLNode(ByRef Nodes As MSXML.IXMLDOMNodeList, _
    ByVal Indent As Integer)

    Dim xNode As MSXML.IXMLDOMNode
    Dim Attr As IXMLDOMAttribute
    Dim Role$
    Indent = Indent + 2
    
    For Each xNode In Nodes
        If xNode.nodeType = NODE_ELEMENT Then
            For Each Attr In xNode.Attributes
                Role = Attr.nodeValue
            Next
        ElseIf xNode.nodeType = NODE_TEXT Then
            Debug.Print Space$(Indent) & xNode.parentNode.nodeName & _
            "(" & Role & ")" & ":" & xNode.nodeValue
        End If
        
        If xNode.hasChildNodes Then
            DisplayXMLNode xNode.childNodes, Indent
        End If
    Next xNode
End Sub

実は、<price currency='dollar'/>  となっているときの事を
考えての事なのですが・・・。
どうも今ひとつ上手くいっていません。
現在はそのような状態です。

※上記のように訂正する前に
最初の質問のプログラムの「→→→→→→」部に
For Each Attr In xNode.Attributes 〜  を
挿入し、属性がprintされるのは確認しました。


初心者  2004-09-16 18:36:10  No: 116320

追記:

> # できれば MSXML1 ではなく、MSXML4 以降を使った方が良いかと。
MSXML4を参照設定しました。
ありがとうございます。

あとはプログラム上の
Dim xDoc As MSXML.DOMDocument  や
Set xDoc = New MSXML.DOMDocument  の
【MSXML】部を【MSXML4】に直せばいいんですよね?


魔界の仮面弁士  2004-09-17 09:05:34  No: 116321

> どうも今ひとつ上手くいっていません。
えぇと。そもそも「どのように Debug.Print したいのか」という、
求めている最終結果がわからないので、コードの修正案を書けません……。

先のXML形式だとしたら、どのような結果が出力されれば良いのでしょうか?

>【MSXML】部を【MSXML4】に直せばいいんですよね?
えーと。そうではなく、 MSXML2.DOMDocument40 となります。
# MSXML4以降のバージョンをお持ちであれば、そちらを使ってもOK。
http://www.interq.or.jp/japan/koi_san/trash/2004/msxml.htm

古いバージョンを使うべきでは無いというのは、その当時は、まだ
XSLT等の規格が定まっていない頃だったので、勧告版の実装ではなく、
ワーキングドラフト版の実装が行われている部分があるためです。
また、古いバージョンにはセキュリティ上の問題もありますし、
後継バージョンなら、XPathの解析速度が向上しているという利点もあります。


初心者  2004-09-17 18:34:57  No: 116322

魔界の仮面弁士  様

たびたびすみません・・・。

> 先のXML形式だとしたら、どのような結果が出力されれば良いのでしょうか?
下記のXMLで下記のような出力を望んでいます。

<cars>
 <car>
    <name>乗用車</name>
    <price currency='dollar'>150</price>
  </car>
  <car>
    <name>トラック</name>
    <price currency='dollar'/>      #←←←最初の質問と変更してます
  </car>
  <car>
    <name>オープンカー</name>
    <price currency='dollar'>200</price>
  </car>
</cars>

※※※※※  希望出力結果  ※※※※※
(データのあるものだけが出力される)
-----------------------------------
name:
     乗用車
price (dollar):
     150
name:
     トラック
name:
     オープンカー
price (dollar):
     200

-----------------------------------------

これにより、実際はこの後 Select Case を使って
VBプログラム中の変数にこのデータを挿入していきたいと
考えています。

また参考までにお聞きしたいのですが、
.nodeValueはStringではないのでしょうか?

::::::::::::::::::::::::::::::::::::::::::::
> えーと。そうではなく、 MSXML2.DOMDocument40 となります。
> # MSXML4以降のバージョンをお持ちであれば、そちらを使ってもOK。
> # http://www.interq.or.jp/japan/koi_san/trash/2004/msxml.htm
今、マシンにはMSXML4が入っているので、VBに参照設定をしました。
MSXML4の使い方などが載っているページがみつけられないのですが、
(MSXML.DOMDocument  →  MSXML2.DOMDocument4.0  となるなどの)
どこか参考になるページ等ご存知でしょうか?

本当に何もわかっていなくて大変申し訳ないです。
お手数ばかりおかけしますが、
よろしくお願い致します。


初心者  2004-09-17 19:05:58  No: 116323

魔界の仮面弁士  様

魔界の仮面弁士さん!ありがとうございます。
やっと頭が動き始めたというか、うまくできました。
本当にありがとうございます。

下記のようにプログラムしてみました。

Public Sub DisplayXMLNode(ByRef Nodes As MSXML.IXMLDOMNodeList, _
    ByVal Indent As Integer, ByVal Role As String)

    Dim xNode As MSXML.IXMLDOMNode
    Dim Attr As IXMLDOMAttribute
    Indent = Indent + 2
    
    For Each xNode In Nodes
        If xNode.nodeType = NODE_ELEMENT Then
            For Each Attr In xNode.Attributes
                Role = Attr.nodeValue
            Next
        ElseIf xNode.nodeType = NODE_TEXT Then
            Debug.Print Space$(Indent) & xNode.parentNode.nodeName & _
            "(" & Role & ")" & ":" & xNode.nodeValue
        End If
        
        If xNode.hasChildNodes Then
            DisplayXMLNode xNode.childNodes, Indent, Role
        End If
        Role = ""
    Next xNode
End Sub

魔界の仮面弁士さんの指導をいただいたおかげです。

MSXML4の使い方についても引き続いて
ご指導いただけると幸いです。


魔界の仮面弁士  2004-09-17 20:45:02  No: 116324

[魔界の仮面弁士 2004/09/16(木) 01:20:08]
> 誤) For Each N In xDoc.selectNodes("//*|//@*")
> 正) For Each N In xDoc.selectNodes("//*|//@*|//text()")
> # コメントノードや処理命令ノードは無視して良いのかな?

もし、コメントノードや処理命令ノードも加えるなら、
  For Each N In xDoc.selectNodes("//*|//@*|//text()|//comment()|//processing-instruction()")
という感じで。

[初心者さん 2004/09/17(金) 09:34:57]
>(データのあるものだけが出力される)
微妙な表現ですね。(^^;)
この場合の『データ』が、何を表しているのかが曖昧です。
# 「<a><b /></a>」というXMLなら、要素ノード"a"は、要素ノード"b"というデータを
# 持っているという見方もできますし…。

とりあえず、この場合の『データ』は、「テキストノード」と「属性値」の事だと
思いますが、その場合、white-space の扱いはどうされますか?

1.「<AA />」
2.「<AA></AA>」
3.「<AA> </AA>」

の場合、XML的には 1 と 2 は等価ですが、3 は別ですよね。
3の場合、空白一つのテキストノードが存在しますから。

今回の場合、空白だけでなく、改行まで含まれていますので、
ほとんどの要素ノードに「データが含まれている」と思いますよ。

[初心者 2004/09/17(金) 09:34:57]
> 下記のような出力を望んでいます。

一応、こちらでもサンプルを書いてみました。
とりあえず、white-spaceのみのテキストノードは無視させていますが、
  <car>
    あ
    <name />
    い
    <price />
    う
  </car>
のような時には、どのような出力結果を望んでいるのかが分からなかったため、
「要素ノードとテキストノードが並んでいる場合」については考慮していません。
このようなケースにも対応させたい場合は、適宜、コードを書き変えてください。

'======================================================
'エラー処理は省略してあります。
Option Explicit

Private Sub Main()
  Dim oDoc As DOMDocument
  Set oDoc = CreateObject("MSXML2.DOMDocument.4.0")
  oDoc.async = False
  oDoc.preserveWhiteSpace = True
  oDoc.Load "C:\Sample.xml"
  oDoc.setProperty "SelectionLanguage", "XPath"

  Dim S As String
  Dim N As IXMLDOMNode
  Dim W As IXMLDOMNode
  For Each N In oDoc.selectNodes("//*[@*]|//*[not(*) and normalize-space(text())!=' ']|//comment()")
    If N.nodeType = NODE_COMMENT Then
      Debug.Print "(コメント):"
      Debug.Print Tab(4); N.nodeValue
    Else
      S = N.nodeName
      For Each W In N.Attributes
        Debug.Print S; "("; W.nodeName; "):"
        '属性値が空の時は、属性名だけ出力する
        If W.nodeValue <> "" Then
          Debug.Print Tab(4); W.nodeValue
        End If
      Next
      If N.Text <> "" Then
        'テキストノードが空の時は、要素名も出力しない
        Debug.Print S; ":"
        Debug.Print Tab(4); N.Text
      End If
    End If
  Next

  Set oDoc = Nothing
End Sub

========== C:\Sample.xml (UTF-8、サイズ=573バイト) ============
<cars>
  <car>
    <name>乗用車</name>
    <price currency="dollar">150</price>
  </car>
  <car>
    <name>トラック</name>
    <price currency="dollar"/><!--テキストノードが無い場合-->
  </car>
  <car>
    <name><![CDATA[オープンカー]]></name><!--CDATAセクションが使用されている場合-->
    <price currency="dollar" currency2="US-dollar">200</price><!--属性が2つある場合-->
  </car>
  <car>
    <name></name><!--空要素だが終了タグを持つ場合-->
    <price currency="" /><!--属性値が空の場合-->
  </car>
</cars>
==================================================================

> MSXML4の使い方などが載っているページがみつけられないのですが、
Microsoft の ダウンロードセンターから、MSXML4 の SDK をダウンロードしてみてください。


魔界の仮面弁士  2004-09-17 21:06:48  No: 116325

書き忘れ。

[初心者さん 2004/09/17(金) 09:34:57]
> また参考までにお聞きしたいのですが、
> .nodeValueはStringではないのでしょうか?
常にString型で受け取りたいなら、text や xml などを
使うこともできます。(若干、意味は変わりますけど)

nodeValue については、String の時もあれば、Null の時もあります。
このプロパティは、SDK を見ていただくと分かりますが、
ノードの種類によって出力結果が異なります。

NODE_TEXT:
  テキストノードの内容をStringで返します。

NODE_ATTRIBUTE:
  属性の値をStringで返します。

NODE_CDATA_SECTION:
  セクションの内容をStringで返します。

NODE_COMMENT:
  コメントの内容をStringで返します。

NODE_PROCESSING_INSTRUCTION:
  処理命令の内容をStringで返します。

それ以外のノードタイプ:
  常に Null が返されます。


初心者  2004-09-18 00:38:45  No: 116326

魔界の仮面弁士 様

ありがとうございます。
大変参考になりました。

本当に色々とありがとうございます。
何から何まで・・。(弟子入りしたいくらいです)

魔界の仮面弁士さんのSampleプログラムを
じっくり噛みしめて、自分なりに消化して
プログラムを完成させたいと思います。

SDKも今読んでます。英語なのでゆっくりですが。(^^;)

本当にありがとうございました。


初心者  2004-09-18 00:44:56  No: 116327

追記:
[魔界の仮面弁士 2004/09/17(金) 11:45:02]
> とりあえず、この場合の『データ』は、「テキストノード」と「属性値」の事だと
> 思いますが、その場合、white-space の扱いはどうされますか?

おっしゃるとおりです。

    <name>乗用車</name>
    <price currency="dollar">150</price>

私の言いたかった「データ」とは
上記XMLでの「乗用車」と「dollar」です。

-----------------------------------------------------------
XMLの解説も丁寧にしていただいて、
本当にありがとうございました。


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

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






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