XMLを読み込むには

解決


うい  2007-06-07 11:48:15  No: 143582

環境はVB.NET WindowsXP Proです。

XMLファイルの中身が下のようになってるものを読もうとしています
<Items> 
  <SampleClass>
    <Number>123</Number>
    <Message>テストです。</Message>
  </SampleClass>
</Items>

1段だけのときはどぼんプログラミング道場さんのサンプルで
わかりました。
http://dobon.net/vb/dotnet/file/xmlserializer.html

2段階に子ノードができてるときの読み方がわかりません。
よろしくお願いします。


うい  2007-06-07 12:11:20  No: 143583

XMLを読み込むためのクラスはこう宣言してだめでした。
    Public Class Items
        Public Class SampleClass
            Public Number As Integer
            Public Message As String
        End Class
    End Class

よろしくお願いします。


うい  2007-06-08 08:42:19  No: 143584

Dim doc As New XmlDocument
        Dim xmlPath = Me.MapPath("Test.xml")

        doc.Load(xmlPath)

        Dim NodeLists As XmlNodeList
        Dim Node As XmlNode

        NodeLists = doc.SelectNodes("//SampleClass[text()='SAmple'])
        For Each Node In NodeLists
            TextBox1.Text = TextBox1.Text & Node.InnerText
        Next

<Items> 
  <SampleClass>Sample</SampleClass>
<TESTNODE>
    <Number>123</Number>
    <Message>テストです。</Message>
<TESTNODE>
  </SampleClass>
</Items>

ソースとXMLファイルを上記にすることでSampleClassの値Sampleが取れるのですが、中の123やテストですがとれません。
子の値をとる方法でヒントでもいいのでよろしくお願いします。


魔界の仮面弁士  2007-06-08 18:33:25  No: 143585

> 中の123やテストですがとれません。
XPath 式はご存じのようなので、SelectNodes 等で取得するとか。
あるいは、特定の XMLNode を取得することまではできているようなので、
そこから ChildNodes, NextSibling 等々で親子兄弟ノードを辿るとか。


うい  2007-06-10 11:52:37  No: 143586

SelectNodesで取得する方法を試しています。
XPathをこのように書き換えてみました。
//SampleClass[text()=Sample]../TESTNODE
または()でくくって
(//SampleClass[text()=Sample])../TESTNODE
とやってみたんですが、トークンが無効です。とエラーがでてしまいます。
XPathの指定方法が悪いのでしょうか。
よろしくお願いします


うい  2007-06-10 20:02:39  No: 143587

XPathの指定で//child::SampleClass[text()='Sample']/parent::*
といったようにparentで親ノードにあがっったり
//child::SampleClass[text()='Sample']/parent::*/Childとすることで
子も見れるようになりました。
ありがとうございました。


うい  2007-06-10 20:02:58  No: 143588

解決のチェックを忘れてました。


魔界の仮面弁士  2007-06-10 20:24:59  No: 143589

> ソースとXMLファイルを上記にすることで
VB コードも XML ファイルも文法違反なので、それでは動かないと思いますよ。
実験用のコードを書いたのであれば、その実際のコードをそのまま貼り付けてください。

> //SampleClass[text()=Sample]../TESTNODE
> (//SampleClass[text()=Sample])../TESTNODE
述部が、"Sampleという文字列"ではなく、"Sample要素ノード" の指定になっているのは
単なる誤記かと思われますが、それにしても、軸部やノードテストの記述が不正です。
(括弧指定は論外として…)

まず、それらのパスは、どのようなノードを取得する目的で書かれた物なのでしょうか?

コードの意図がわかれば、代替コードも示せるのですが、何しろ、元になる XML データすら
正しくありませんでしたので、具体例を示しにくいですのが、一応、
  //SampleClass[text()='Sample']/../TESTNODE
  //TESTNODE[../SampleClass/text()='SampleClass']
  //TESTNODE[../SampleClass='SampleClass']
といった構文ならば文法違反にはなりません。そちらの意図にあっているかは不明ですが。

で。基本的には「軸::ノードテスト[述語]」をスラッシュで区切っていけば OK です。
(角括弧で囲まれる述語の部分は、すでに御存知ですよね)

このとき、「軸::」は省略形が使われる事が多く、
  child::  →  「」記述しない。
  descendant::  →  「//」スラッシュ2つ。
  self::  →  「.」ドット。
  parent::  →  「..」ドット2つ。
などと書かれるのが一般的です。ただし、軸によっては省略形が用意されていないものも
ありますので、できれば正しい記述方法を学んでおかれた方がよろしいかと。

とりあえず、(2 回目の XML は不正なので)は、最初の XML データを例として使わせてもらうと、
単に「テストです。」という文字列を得るためにも、いろいろな構文を使う事ができます。

たとえば、[文書ノード]-[Items要素ノード]-[SampleClass要素ノード]-[Message要素ノード]の
テキストノードのうち、最初に見つかった物を得ると考えれば、
  S = doc.SelectSingleNode("Items/SampleClass/Message/text()").Value
  S = doc.SelectSingleNode("Items/SampleClass/Message").InnerText
  S = doc.SelectNodes("Items/SampleClass/Message/text()")(0).Value
  S = doc.SelectNodes("Items/SampleClass/Message")(0).InnerText
といった構文を使うことができます。

あるいは子孫ノードのうち、最初のMessage要素ノードを取り出すと考えれば、
  S = doc.SelectSingleNode("descendant::Message[1]/text()").Value
  S = doc.SelectSingleNode("descendant::Message/text()").Value
  S = doc.SelectSingleNode("descendant::Message").InnerText
  S = doc.SelectSingleNode("//Message/text()").Value
  S = doc.SelectSingleNode("//Message").InnerText
などとも書けます。

あるいは、Number 要素を ID のように扱い、「数値 123 に対応するメッセージを取得する」と考えるなら、
  S = doc.SelectSingleNode("descendant::Number[number(self::node())=123]/parent::SampleClass/child::Message/child::text()").Value
  S = doc.SelectSingleNode("descendant::Number[number(self::node())=123]/parent::SampleClass/Message/text()").Value
  S = doc.SelectSingleNode("//Number[number(.)=123]/../Message/text()").Value
  S = doc.SelectSingleNode("//Number[number()=123]/../Message/text()").Value
  S = doc.SelectSingleNode("//Number[text()='123']/../Message/text()").Value
  S = doc.SelectSingleNode("//Number[.='123']/ancestor::node()//Message/text()").Value
  S = doc.SelectSingleNode("//Number[.='123']/ancestor::node()//Message").InnerText
  S = doc.SelectSingleNode("//Number[number(.)=123]/../Message/text()").Value
  S = doc.SelectSingleNode("descendant::Message[parent::node()/child::Number/child::text()='123']/text()").Value
  S = doc.SelectSingleNode("descendant::Message[parent::node()/child::Number/text()='123']/text()").Value
  S = doc.SelectSingleNode("descendant::Message[parent::node()/descendant::text()='123']/text()").Value
  S = doc.SelectSingleNode("//Message[../descendant::*='123']/text()").Value
  S = doc.SelectSingleNode("//Message[../Number/text()='123']/text()").Value
  S = doc.SelectSingleNode("//Message[../Number/text()=123]/text()").Value
  S = doc.SelectSingleNode("//Message[../Number='123']").InnerText
などとも書けます。


うい  2007-06-10 22:28:50  No: 143590

'XMLファイルです
<?xml version="1.0" encoding="utf-8" ?>
<RootEle>
  <Ele>
    <Node1>TestNode1</Node1>
    <Node2>
      <Child1>TestNode1_Child1</Child1>
      <Child2>TestNode1_Child2</Child2>
    </Node2>
  </Ele>
  <Ele>
    <Node1>TestNode1</Node1>
    <Node2>
      <Child1>TestNode1_Child1</Child1>
      <Child2>TestNode1_Child2</Child2>
    </Node2>
  </Ele>
</RootEle>

'ソースファイル
        Dim StrChild1 As String
        Dim StrChild2 As String

        'XPath式の代入
        Dim SelectNodesXPath As String

        Dim doc As XmlDocument = New XmlDocument
        'XmlPathにはファイルの絶対パスが入ってる
        doc.Load(xmlPath)

        'ノードの中に"が出てきたときの対策
        SelectNodesXPath = "//child::Ele/child::Node1[text()='TestNode1']/parent::*/child::Node2/child::*"

        'Node2以下をノードリストに格納
        Dim NodeList As XmlNodeList = doc.SelectNodes(SelectNodesXPath)

        '子ノードいるのか検索
        If NodeList.Item(0).HasChildNodes Then

            'この部分は要素が増える予定です。
            For Each Node As XmlNode In NodeList

                Select Case Node.LocalName
                    Case "Child1"
                        '要素内のテキストが入る
                        StrChild1 = Node.InnerText

                    Case "Child2"
                        '要素内のテキストが入る
                        StrChild2 = Node.InnerText

                End Select
            Next
        Else
            '子ノードが存在しない

        End If

StrChild1 ,StrChild2に Node1内要素の値をいれるサンプルです。
よろしくお願いします。


うい  2007-06-10 22:31:01  No: 143591

一番最初のNode2の子要素Child1、Child2を入れるサンプルでした。


うい  2007-06-10 22:36:46  No: 143592

魔界の仮面弁士さんありがとうございます。
XPathの正式な方法を知らなかったのでとても勉強になりました。

動かない状態での載せてしまい、返信をいただくのに大変お手数おかけしました。
今度は動く状態のものをそのまま貼り付けてみました。
よろしくお願いします。


うい  2007-06-10 22:36:46  No: 143593

魔界の仮面弁士さんありがとうございます。
XPathの正式な方法を知らなかったのでとても勉強になりました。

動かない状態での載せてしまい、返信をいただくのに大変お手数おかけしました。
今度は動く状態のものをそのまま貼り付けてみました。
よろしくお願いします。


魔界の仮面弁士  2007-06-11 11:38:13  No: 143594

今度は、肝心な質問内容が抜けているような…。

サンプルを実行すると、「Dim NodeList As XmlNodeList」に、
  <Child1>TestNode1_Child1</Child1>
  <Child2>TestNode1_Child2</Child2>
  <Child1>TestNode1_Child1</Child1>
  <Child2>TestNode1_Child2</Child2>
の 4 つの要素ノードが格納されるであろうという事は分かりますが、
それについて、何を回答すれば良いでしょうか。

> よろしくお願いします。

いや、よろしくって……何を? (^^;

> 'ノードの中に"が出てきたときの対策
このコメントの意味が分からなかったのですが、どういう意味でしょうか?

> SelectNodesXPath = "//child::Ele/child::Node1[text()='TestNode1']/parent::*/child::Node2/child::*"
他の表現としては、
  SelectNodesXPath = "//Ele/Node2[preceding-sibling::Node1='TestNode1']/node()"
  SelectNodesXPath = "//Node2[../Node1='TestNode1']/node()"
なども使えますね。


うい  2007-06-18 09:04:11  No: 143595

返信がおそくなってすいません。
魔界の仮面弁士さんがいろいろ教えてくださって完成しました。
文法は変なのにたまたま動いてる可能性もあったので、ソースを見ていただけるとありがたいです。お願いします。
のつもりでした。

前後の文脈が抜けてるせいで読みにくい文章になってすいません。
また何かありましたら、質問いたしますので
これからもよろしくお願いします。


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

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






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