XMLの値
投稿者 mayopee  (社会人)
投稿日時
2018/8/31 12:29:31
すみません。環境を忘れていました。
VB2010です。
VB2010です。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2018/9/1 00:39:34
「どういう結果を得たいのか」は説明されているのですが、
肝心の「どの部分の情報を取得したいのか」の説明が不足しているようで…。
> ●ID要素は属性のみなので値としてはString.Emptyが欲しい
ID 要素は空要素では無いですよね。
ここでいう『値』とは何を指していますか?
とりあえず、ID 要素の直下にある Text ノードのみを得たいという意味で良いでしょうか?
もしそれで構わないなら、直下の XText を繋げてみるのは如何でしょう。たとえば
とすることができます。
(このままだと冗長的なので、実際に使う時は拡張メソッドにしておいた方が良いかも)
ただし、元の空白を保持させておいた場合には、
改行や空白を含んだ文字列になりえることにご注意ください。
> Dim element1 As XElement = Doc.<Root>.<ID>.First
> Console.WriteLine(String.Format("タグ名:{0} 値:{1}", element1.Name, element1.Value))
ということで、空白の扱いについて。
───────
今回は埋め込みの XDocument リテラルで記述しておられるようなので、
一部の改行やインデント空白が破棄され、element1 は下記の内容になります。
element1.Attributes().Count() は『1』を返します。
element1.Attributes()(0) は Attribute ノード『id="1"』を返します。
element1.Nodes().Count() は 『1』 を返します。
element1.Nodes()(0) は Element ノードです。これは element2 変数が示す XElement にあたります。
element2 には、下記の Element ノードが入ります。
そして上記の element2 は、直下に 2 つの子ノードを持っています。
Text ノード『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
Element ノード『<Age>40<Age>』
が得られるという想定です。
この場合、直下の XTextのみを拾って繋げると、
element1 からは『』すなわち String.Empty
element2 からは『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
が得られるという想定です。
───────
もしも XDocument が LoadOptions.PreserveWhitespace 指定で読み込まれていた場合、
上記の element1 には下記の Element ノードが入ることになります。
element1.Attributes().Count() は『1』を返します。
element1.Attributes()(0) は Attribute ノード『id="1"』を返します。
element1.Nodes().Count() は 『3』 を返します。
element1.Nodes()(0) は Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』を指します。
element1.Nodes()(1) は Element ノードです。これは element2 変数が示す XElement にあたります。
element1.Nodes()(2) は Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣』を指します。
element2 には、下記の Element ノードが入ります。
そして上記の element2 は、直下に 3 つの子ノードを持っています。
Text ノード『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
Element ノード『<Age>40<Age>』
Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
この場合、直下の XTextのみを繋げると、
element1 からは『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣{改行}␣␣␣␣␣␣␣␣␣␣␣』
element2 からは『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
が得られるという想定です。
肝心の「どの部分の情報を取得したいのか」の説明が不足しているようで…。
> ●ID要素は属性のみなので値としてはString.Emptyが欲しい
ID 要素は空要素では無いですよね。
ここでいう『値』とは何を指していますか?
とりあえず、ID 要素の直下にある Text ノードのみを得たいという意味で良いでしょうか?
もしそれで構わないなら、直下の XText を繋げてみるのは如何でしょう。たとえば
Dim value = String.Join("", element1.Nodes().OfType(Of XText)().Select(Function(n) n.Value))
とすることができます。
(このままだと冗長的なので、実際に使う時は拡張メソッドにしておいた方が良いかも)
ただし、元の空白を保持させておいた場合には、
改行や空白を含んだ文字列になりえることにご注意ください。
> Dim element1 As XElement = Doc.<Root>.<ID>.First
> Console.WriteLine(String.Format("タグ名:{0} 値:{1}", element1.Name, element1.Value))
ということで、空白の扱いについて。
───────
今回は埋め込みの XDocument リテラルで記述しておられるようなので、
一部の改行やインデント空白が破棄され、element1 は下記の内容になります。
『<ID id="1">
<Name>山田太郎
<Age>40</Age></Name>』
<Name>山田太郎
<Age>40</Age></Name>』
element1.Attributes().Count() は『1』を返します。
element1.Attributes()(0) は Attribute ノード『id="1"』を返します。
element1.Nodes().Count() は 『1』 を返します。
element1.Nodes()(0) は Element ノードです。これは element2 変数が示す XElement にあたります。
element2 には、下記の Element ノードが入ります。
『<Name>山田太郎
<Age>40</Age></Name>』
<Age>40</Age></Name>』
そして上記の element2 は、直下に 2 つの子ノードを持っています。
Text ノード『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
Element ノード『<Age>40<Age>』
が得られるという想定です。
この場合、直下の XTextのみを拾って繋げると、
element1 からは『』すなわち String.Empty
element2 からは『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
が得られるという想定です。
───────
もしも XDocument が LoadOptions.PreserveWhitespace 指定で読み込まれていた場合、
上記の element1 には下記の Element ノードが入ることになります。
『<ID id="1">
<Name>山田太郎
<Age>40</Age>
</Name>
</ID>』
<Name>山田太郎
<Age>40</Age>
</Name>
</ID>』
element1.Attributes().Count() は『1』を返します。
element1.Attributes()(0) は Attribute ノード『id="1"』を返します。
element1.Nodes().Count() は 『3』 を返します。
element1.Nodes()(0) は Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』を指します。
element1.Nodes()(1) は Element ノードです。これは element2 変数が示す XElement にあたります。
element1.Nodes()(2) は Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣』を指します。
element2 には、下記の Element ノードが入ります。
『<Name>山田太郎
<Age>40</Age>
</Name>』
<Age>40</Age>
</Name>』
そして上記の element2 は、直下に 3 つの子ノードを持っています。
Text ノード『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
Element ノード『<Age>40<Age>』
Text ノード『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
この場合、直下の XTextのみを繋げると、
element1 からは『{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣{改行}␣␣␣␣␣␣␣␣␣␣␣』
element2 からは『山田太郎{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣{改行}␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣』
が得られるという想定です。
投稿者 mayopee  (社会人)
投稿日時
2018/9/1 09:00:30
魔界の仮面弁士様、説明不足にも関わらず意図を汲んで下さり、ありがとうございます。
教えて頂いた方法で希望する結果を得られました。DOMの方もOfType(Of XText)の部分を
OfType(Of XmlText)に変更することで同じ結果が得られました。
早速、拡張メソッドを作って自作ライブラリに登録しました。
原因は自分が親子関係を理解できていない為だと思います。
XElementの下に目視では確認できないXNodeがぶらさがっているのが解りませんでした。
DOMでノードを列挙した時も#textという名前のTextノードが列挙されて意味不明でしたが
これで納得しました。
又、改行や空白についてもファイルからLoadしてLoadOptions.PreserveWhitespaceを
指定して確認しました。解説通りの結果となる事を確認しました。
所で、話題は少々ずれますが、今回の質問でもXDocument(以下:LINQ式)、XmlDocument(以下:DOM式)両方で質問しました。
自分はほとんどLINQ式を使いますが、DOM式にしかできない事、又DOM式がLINQ式
に比べて優位な部分があるのでしょうか?
教えて頂いた方法で希望する結果を得られました。DOMの方もOfType(Of XText)の部分を
OfType(Of XmlText)に変更することで同じ結果が得られました。
早速、拡張メソッドを作って自作ライブラリに登録しました。
原因は自分が親子関係を理解できていない為だと思います。
XElementの下に目視では確認できないXNodeがぶらさがっているのが解りませんでした。
DOMでノードを列挙した時も#textという名前のTextノードが列挙されて意味不明でしたが
これで納得しました。
又、改行や空白についてもファイルからLoadしてLoadOptions.PreserveWhitespaceを
指定して確認しました。解説通りの結果となる事を確認しました。
所で、話題は少々ずれますが、今回の質問でもXDocument(以下:LINQ式)、XmlDocument(以下:DOM式)両方で質問しました。
自分はほとんどLINQ式を使いますが、DOM式にしかできない事、又DOM式がLINQ式
に比べて優位な部分があるのでしょうか?
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2018/9/3 00:35:24
> 解説通りの結果となる事を確認しました。
後半の解説が一部間違っていて申し訳ありません。
いずれにせよ、互いの意図は伝わったようで何よりです。(^^;
VB2010 ではなく、最近の環境であれば、
Dim value1 As String = xmlDoc.SelectSingleNode("/Root/ID/text()")?.Value
Dim value2 As String = xmlDoc.SelectSingleNode("/Root/ID/Name/text()")?.Value
という手も使えたかも知れません…。
> Textノードが列挙されて意味不明でしたが
> これで納得しました。
場合によっては CDATA セクション、コメント、処理命令などの
扱いも考える必要がありますが、今のところは大丈夫そうですね。
> 自分はほとんどLINQ式を使いますが、DOM式にしかできない事、又DOM式がLINQ式
> に比べて優位な部分があるのでしょうか?
XmlDocument は、CLR1 環境でも動作する、という程度の優位点ぐらいしか
思い当たらないですね。
こちらは W3C DOM Level 1 Core および Level 2 Core に準拠した
標準的な API で、古いバージョンの .NET Framework でも動作します。
一方、XDocument は、XLinq すなわち LINQ to XML のために
再設計されたもので、後発だけに使いやすくなっています。
IXmlLineInfo を通じて行番号と行位置を得ることもできますし、
XML 名前空間の扱いについても、こちらの方が扱いやすいかと。
https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/concepts/linq/linq-to-xml-vs-dom
https://blogs.msdn.microsoft.com/codejunkie/2008/10/08/xmldocument-vs-xelement-performance/
ただし、XPath を使う場合においては、私は DOM 版の実装である
SelectSingleNode / SelectNodes の方が好みです。
XLinq 版も、System.Xml.XPath 名前空間に、
XPathSelectElement(s) / XPathEvaluate の拡張メソッドが
用意されていますが、こちらは若干使いにくく感じています。
というのも、DOM 版の XPath は XmlNode を返すので
"/Root/ID/@id" や "/Root/ID/text()" を指定できるのですが、
XLinq 版の方は XNode ではなく XElement なので、
エラーになってしまうから…。
かといって XPathEvaluate だとレイトバインドになってしまうし。
後半の解説が一部間違っていて申し訳ありません。
いずれにせよ、互いの意図は伝わったようで何よりです。(^^;
VB2010 ではなく、最近の環境であれば、
Dim value1 As String = xmlDoc.SelectSingleNode("/Root/ID/text()")?.Value
Dim value2 As String = xmlDoc.SelectSingleNode("/Root/ID/Name/text()")?.Value
という手も使えたかも知れません…。
> Textノードが列挙されて意味不明でしたが
> これで納得しました。
場合によっては CDATA セクション、コメント、処理命令などの
扱いも考える必要がありますが、今のところは大丈夫そうですね。
> 自分はほとんどLINQ式を使いますが、DOM式にしかできない事、又DOM式がLINQ式
> に比べて優位な部分があるのでしょうか?
XmlDocument は、CLR1 環境でも動作する、という程度の優位点ぐらいしか
思い当たらないですね。
こちらは W3C DOM Level 1 Core および Level 2 Core に準拠した
標準的な API で、古いバージョンの .NET Framework でも動作します。
一方、XDocument は、XLinq すなわち LINQ to XML のために
再設計されたもので、後発だけに使いやすくなっています。
IXmlLineInfo を通じて行番号と行位置を得ることもできますし、
XML 名前空間の扱いについても、こちらの方が扱いやすいかと。
https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/concepts/linq/linq-to-xml-vs-dom
https://blogs.msdn.microsoft.com/codejunkie/2008/10/08/xmldocument-vs-xelement-performance/
ただし、XPath を使う場合においては、私は DOM 版の実装である
SelectSingleNode / SelectNodes の方が好みです。
XLinq 版も、System.Xml.XPath 名前空間に、
XPathSelectElement(s) / XPathEvaluate の拡張メソッドが
用意されていますが、こちらは若干使いにくく感じています。
というのも、DOM 版の XPath は XmlNode を返すので
"/Root/ID/@id" や "/Root/ID/text()" を指定できるのですが、
XLinq 版の方は XNode ではなく XElement なので、
エラーになってしまうから…。
かといって XPathEvaluate だとレイトバインドになってしまうし。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2018/9/3 00:52:35
> XLinq 版の方は XNode ではなく XElement なので、
…と思ったけど、XPathNavigator 経由にしてしまえば良いのかな。
どっちにしても VB2015 未満だと、NULL条件演算子( ?. ) が使えないので扱いにくいですが。
…と思ったけど、XPathNavigator 経由にしてしまえば良いのかな。
どっちにしても VB2015 未満だと、NULL条件演算子( ?. ) が使えないので扱いにくいですが。
Dim value1 As String = Doc.CreateNavigator().SelectSingleNode("/Root/ID/text()")?.Value
Dim value2 As String = Doc.CreateNavigator().SelectSingleNode("/Root/ID/Name/text()")?.Value
投稿者 mayopee  (社会人)
投稿日時
2018/9/3 14:52:48
魔界の仮面弁士様、返信ありがとうございます。
XmlDocument版
>Dim value2 As String = xmlDoc.SelectSingleNode("/Root/ID/Name/text()")?.Value
XDocument版
>Dim value2 As String = Doc.CreateNavigator().SelectSingleNode("/Root/ID/Name/text()")?.Value
XPath式でTextノードを得るには、こんな書き方もできるのですね。
勉強になります。VB2010ですので、ご指摘通り「?.」の部分でコンパイラに怒られましたが
「?.」を消去すればTextノードがNothingでない要素については希望する結果を得られました。
NULL条件演算子( ?. )、とても便利そうですね!
>場合によっては CDATA セクション、コメント、処理命令などの
コメント以外、使った事も見た事もありません。CDATA セクションについては、Webで検索して
自分でXMLを作成する場合でも使えそうだと思いました。めったと、禁止文字を使う事はないけど
エスケープ文字はなんだっけ?となるよりいいかなという感想です。
DOM式とLINQ式についても、ご意見を頂き、ありがとうございます。
自分の場合、LINQ式を使う理由としてはXMLを読み込んで要素を編集、抽出する場合にLINQが
使えるXDocument/XElementの方が圧倒的に使い勝手が良い、コードも簡潔になって見通しが良い
という理由からです。
でも、今回ご指摘いただいたOfTypeやCastを使えばDOM式でもSelectやWhereが使えるのですね。
話題がちょっと脱線しましたが、当初の疑問は解決しましたので、これで解決とさせていだだきます。
私の発言に反応があるかもしれないので、解決マークはしばらく未チェックにしておきます。
XmlDocument版
>Dim value2 As String = xmlDoc.SelectSingleNode("/Root/ID/Name/text()")?.Value
XDocument版
>Dim value2 As String = Doc.CreateNavigator().SelectSingleNode("/Root/ID/Name/text()")?.Value
XPath式でTextノードを得るには、こんな書き方もできるのですね。
勉強になります。VB2010ですので、ご指摘通り「?.」の部分でコンパイラに怒られましたが
「?.」を消去すればTextノードがNothingでない要素については希望する結果を得られました。
NULL条件演算子( ?. )、とても便利そうですね!
>場合によっては CDATA セクション、コメント、処理命令などの
コメント以外、使った事も見た事もありません。CDATA セクションについては、Webで検索して
自分でXMLを作成する場合でも使えそうだと思いました。めったと、禁止文字を使う事はないけど
エスケープ文字はなんだっけ?となるよりいいかなという感想です。
DOM式とLINQ式についても、ご意見を頂き、ありがとうございます。
自分の場合、LINQ式を使う理由としてはXMLを読み込んで要素を編集、抽出する場合にLINQが
使えるXDocument/XElementの方が圧倒的に使い勝手が良い、コードも簡潔になって見通しが良い
という理由からです。
でも、今回ご指摘いただいたOfTypeやCastを使えばDOM式でもSelectやWhereが使えるのですね。
話題がちょっと脱線しましたが、当初の疑問は解決しましたので、これで解決とさせていだだきます。
私の発言に反応があるかもしれないので、解決マークはしばらく未チェックにしておきます。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2018/9/3 18:15:16
> コードも簡潔になって見通しが良いという理由からです。
XDocument を使った場合の利点として、XML IntelliSense のサポートも挙げられるかも。
C# にはない VB ならではの魅力ですね。
https://msdn.microsoft.com/library/bb531325%28vs.120%29.aspx
今回の質問にある XML に対応させると、XML スキーマ(*.xsd)はこんな感じかな…?
XDocument を使った場合の利点として、XML IntelliSense のサポートも挙げられるかも。
C# にはない VB ならではの魅力ですね。
https://msdn.microsoft.com/library/bb531325%28vs.120%29.aspx
今回の質問にある XML に対応させると、XML スキーマ(*.xsd)はこんな感じかな…?
<xs:schema
targetNamespace="urn:mayopee"
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Root">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" maxOccurs="unbounded" minOccurs="0">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="Name">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:byte" name="Age"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:unsignedInt" name="id" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
投稿者 mayopee  (社会人)
投稿日時
2018/9/4 09:00:50
>XDocument を使った場合の利点として、XML IntelliSense のサポートも挙げられるかも。
おお、すばらしい。コーディング、めっちゃ楽ですね。
魔界の仮面弁士様、どうもありがとうございました。
これにて解決とさせていただきます。
おお、すばらしい。コーディング、めっちゃ楽ですね。
魔界の仮面弁士様、どうもありがとうございました。
これにて解決とさせていただきます。
以下のコードでは、いずれもinnerTextである「山田太郎 40」が返却されます。
希望する値は以下の通りです。
●ID要素は属性のみなので値としてはString.Emptyが欲しい
●Name要素は「山田太郎」のみが欲しい
XMLの設計がまずいということは置いといて、もし以下のような構造のXMLが
あったらという事で、お願いします。