VB.NET で XML ファイルを読み込む方法の一例。
俺は繰り返されるブロック単位でノードとして読み込み処理するが、頭から一行ずつ読んで自分であれこれする方法もあるようだ。
というわけで、自分のやり方の備忘録。
処理するのはこんなXMLデータ。<支店請求>という支店別の大きなブロックの中に、<請求伝票>という伝票(請求書)単位のブロックがあり、その中に明細1行の情報をまとめた<明細>というブロックがある。
単価、個数は請求金額の属性としてもっている。
実際の請求データはもっと項目があるが、一番シンプルなこの形で処理を記録しておく。
<支店別請求>
<支店情報>
<支店ID>0001</支店ID>
<支店名>あああ支店</支店名>
</支店情報>
<請求伝票>
<請求ID>202203090001</請求ID>
<請求合計金額>2300</請求合計金額>
<明細>
<明細行No>01</明細行No>
<商品ID>A0001</商品ID>
<商品名>あらら電池</商品名>
<請求金額 単価="25" 個数="40">1000</請求金額>
</明細>
<明細>
<明細行No>02</明細行No>
<商品ID>A0981</商品ID>
<商品名>小型クリーナー</商品名>
<請求金額 単価="1300" 個数="1">1300</請求金額>
</明細>
</請求伝票>
<請求伝票>
<請求ID>202203090002</請求ID>
<請求合計金額>10500</請求合計金額>
<明細>
<明細行No>01</明細行No>
<商品ID>B0055</商品ID>
<商品名>ふんどし型カツラ</商品名>
<請求金額 単価="100" 個数="105">10500</請求金額>
</明細>
</請求伝票>
</支店別請求>
<支店別請求>
<支店情報>
<支店ID>0003</支店ID>
<支店名>ううう支店</支店名>
</支店情報>
<請求伝票>
<請求ID>202203090201</請求ID>
<請求合計金額>10500</請求合計金額>
<明細>
<明細行No>01</明細行No>
<商品ID>B0055</商品ID>
<商品名>ふんどし型カツラ</商品名>
<請求金額 単価="100" 個数="105">10500</請求金額>
</明細>
</請求伝票>
</支店別請求>
この XML データを処理し、下記内容でテーブルに突っ込む。
0001|あああ支店|202203090001|2300|01|A0001|あらら電池|25|40|10000001|あああ支店|202203090001|2300|02|A0981|小型クリーナー|1300|1|13000001|あああ支店|202203090002110500|01|B0055|ふんどし型カツラ|100|105|105000003|ううう支店|202203090201|10500|01|B0055|ふんどし型カツラ|100|105|10500
まず、XML ファイルを読み込む(Imports System.Xml は忘れずに)。
Dim jd As New XmlDocument()
Try jd.Load("受信ファイル.xml") Catch ex As Exception 'エラー処理 End Try
で、「ルート要素の取り出し」を。全てのデータの取り込みね。
Dim xroot As XmlNode xroot = jd.DocumentElement
そして、親、子、孫のブロックを順次取り出しながら処理をしていく。
(値をセットしていくテーブルはすでに設定されているってことで。行データを格納するオブジェクトは newRow)
Dim xlist1 As XmlNodeList ' <支店別請求>のリスト Dim xlist2 As XmlNodeList ' <請求伝票>のリスト Dim xlist3 As XmlNodeList ' <明細>のリスト Dim xnode1 As XmlNode ' <支店別請求>のリストの中のひとつの<支店別請求> Dim xnode2 As XmlNode ' <請求伝票>のリストの中のひとつの<請求伝票> Dim xnode3 As XmlNode ' <明細>のリストの中のひとつの<明細>
とりあえず親、子、孫の 3階層しかない入れ子なのでそれぞれオブジェクトを設定。もっと階層が深ければ配列にしてもええじゃろう。
また、NULL値とか変なデータは無い前提で。必要なら、
If(String.IsNullOrEmpty(xnode1.SelectSingleNode("支店情報/支店ID").InnerText), "", xnode1.SelectSingleNode("支店情報/支店ID").InnerText)
とかして。
また、全ての項目は必須項目で、必ず全てのタグが存在しているものとする。
xlist1 = xroot.SelectNodes("//支店別請求")
'<支店別請求>の 1ブロックを読み込む For Each xnode1 In xlist1
w_支店ID = xnode1.SelectSingleNode("支店情報/支店ID").InnerText w_支店名 = xnode1.SelectSingleNode("支店情報/支店名").InnerText
xlist2 = xnode1.SelectNodes("請求伝票")
'<支店別請求>の中の<請求伝票>の 1ブロックを読み込む For Each xnode2 In xlist2
w_請求ID = xnode2.SelectSingleNode("請求ID").InnerText w_請求合計金額 = xnode2.SelectSingleNode("請求合計金額").InnerText
xlist3 = xnode2.SelectNodes("明細")
'<請求伝票>の中の<明細>の 1ブロックを読み込む For Each xnode3 In xlist3
Dim newRow = Table1.NewRow
newRow("支店ID") = w_支店ID newRow("支店名") = w_支店名 newRow("請求ID") = w_請求ID newRow("請求合計金額") = w_請求合計金額
newRow("明細行No") = xnode3.SelectSingleNode("明細行No").InnerText newRow("商品ID") = xnode3.SelectSingleNode("商品ID").InnerText newRow("商品名") = xnode3.SelectSingleNode("商品名").InnerText
newRow("単価") = xnode3.SelectSingleNode("請求金額").Attributes(0).InnerText newRow("個数") = xnode3.SelectSingleNode("請求金額").Attributes(1).InnerText newRow("請求金額") = xnode3.SelectSingleNode("請求金額").InnerText
Table1.Rows.Add(newRow)
Next
Next
Next
みたいな感じでいかがか?
ちなみに、ノードの属性値を「属性名」指定で引っ張れないかは調べてない。(Attributes(0)ではなく「単価」という名前で引けないか)
識者の方、ぜひご教授を。
https://docs.microsoft.com/en-us/dotnet/api/system.xml.xmlnode.selectsinglenode?view=net-7.0
>Selects the first XmlNode that matches the 【XPath expression】.
https://triple-underscore.github.io/XML/xpath10-ja.html#location-paths
>attribute::name は文脈ノードの name 属性を選択する。 attribute::name selects the name attribute of the context node
https://triple-underscore.github.io/XML/xpath10-ja.html#path-abbrev
>@name は文脈ノードの name 属性を選択する。