目次
Linq to XML
のお勉強。
XObject
全ての派生型の親クラス。 とりあえず継承ツリーだけ覚えとく。
XObject
├XAttribute : XAttributeはノードじゃないよ
└XNode
├XComment
├XDocumentType
├XContainer
│ ├XElement
│ └XDocument
├XText
│ └XCData
└XProcessingInstruction
ファイルや文字列から XElement
や XDocument
を生成
//
// ファイルから読み込み
//
XDocument xdoc = XDocument.Load("c:/ファイルパス.xml");
XElement xelement = XElement.Load("同上。Streamなども使える");
//
// テキストを読み込み
//
XDocument doc = XDocument.Parse("XML文字列");
XElement ele = XElement.Parse("同上", LoadOptions.PreserveWhitespace);
XMLが不正な場合は XmlException
が投げられる
- LINQ to XML 備忘録1 XDocument, XElement を使用したXML, エンティティクラスの作成と選択処理、 XML ファイルの変換 - Netplanetes : XMLのサイズが大きい場合はXmlReaderでブロック単位で読み込む
XDocument
でXMLを生成する
//
// 簡潔にルートノードだけ
//
XNamespace xmlns = "http://fernweh.jp/";
XName xname = xmlns + "MyRoot";
XDocument xdoc = new XDocument(
new XDeclaration(version: "1.0", encoding: "utf-8", standalone: null),
new XElement(xmlns + "MyRoot"));
// XML宣言部分とXML部分を一緒に取得する方法はあるのだろうか
Trace.WriteLine(xdoc.Declaration); // <?xml version="1.0" encoding="utf-8"?>
Trace.WriteLine(xdoc); //<MyRoot xmlns="http://fernweh.jp/" />
//
// 属性やテキストノード, CDATA
//
var xdoc = new XDocument(
new XElement("A",
new XAttribute("b", "c"),
new XElement("D",
new XText("text"),
new XCData("cdata")
)));
Trace.Write(xdoc); // <A b="c">\n<D>text<![CDATA[cdata]]></D>\n</A>
//
// ファイルに書き出すときは
//
xdoc.Save("ココにStreamやファイルパス, TextWriter, XmlWriterを指定");
要素の操作
//
// 追加や削除や値の変更, 取得
//
var a = new XElement("a");
var b = new XElement("b");
var c = new XElement("c");
/*
Add系 : これらは全て可変長引数で受け取れる
Add
AddFirst
AddAfterSelf
AddBeforeSelf
*/
a.Add(b);
a.Add(c);
Trace.WriteLine(a); // <a><b /><c /><c /></a>
/*
各種プロパティ
NodeType
Name - string ではなく XName型
Value
Parent
PreviousNode
NextNode
FirstNode
FirstAttribute
LastNode
LastAttribute
IsEmpty
空文字のテキストノードのみを持つ要素は空と判定されない
属性は関係ない(<a b="c" /> は 空と判定される)
EmptySequence(.NET 4.5~)
HasAttributes
HasElements
*/
Trace.WriteLine(a.Name); // a
Trace.WriteLine(a.Value); // (空文字)
Trace.WriteLine(a.Parent); // (null)
Trace.WriteLine(a.NodeType
== XmlNodeType.Element); // True
Trace.WriteLine(a.PreviousNode); // (null)
Trace.WriteLine(a.NextNode); // (null)
Trace.WriteLine(a.FirstNode); // <b />
Trace.WriteLine(a.LastNode); // <c />
Trace.WriteLine(a.HasAttributes); // False
Trace.WriteLine(a.HasElements); // True
Trace.WriteLine(a.IsEmpty); // False
Trace.WriteLine(b.Parent); // <a><b /><c /></a>
Trace.WriteLine(b.PreviousNode); // (null)
Trace.WriteLine(b.NextNode); // <c />
Trace.WriteLine(b.FirstNode); // (null)
Trace.WriteLine(b.HasElements); // False
Trace.WriteLine(b.IsEmpty); // True
Trace.WriteLine(c.PreviousNode); // <b />
Trace.WriteLine(c.NextNode); // (null)
/*
Set系
SetValue - nullを入れると ArgumentNullException
SetElementValue - null をセットすると要素が削除される!
SetAttribute - null をセットすると属性が削除される!
*/
a.Element("c").SetValue("m");
Trace.WriteLine(a); // <a><b /><c>m</c></a>
Trace.WriteLine(c.IsEmpty); // False
c.Remove();
Trace.WriteLine(a); // <a><b /></a>
a.SetElementValue("c", string.Empty); /* 要素が存在しない場合は作成 */
Trace.WriteLine(a); // <a><b /><c></c></a>
a.SetElementValue("c", null); /* null をセットすると削除 */
Trace.WriteLine(a); // <a><b /></a>
b.SetValue("x");
b.SetAttributeValue("y", "z");
Trace.WriteLine(a); // <a><b y="z">x</b></a>
Trace.WriteLine(b.FirstNode); // x
Trace.WriteLine(b.FirstNode.NodeType
== XmlNodeType.Text); // True
b.SetAttributeValue("y", "zzz");
Trace.WriteLine(a); // <a><b y="zzz">x</b></a>
/*
属性の取得
Attribute("属性名")
Attributes()
FirstAttribute
LastAttribute
属性の削除
RemoveAttributes()
SetAttributeValue("属性名", null)
*/
b.Attribute("y").SetValue("zzzzzzz");
Trace.WriteLine(a); // <a><b y="zzzzzzz">x</b></a>
b.SetAttributeValue("m", "n"); /* 属性が存在しなければ作成 */
Trace.WriteLine(a); // <a><b y="zzzzzzz" m="n">x</b></a>
b.SetAttributeValue("m", null); /* null をセットすると削除 */
Trace.WriteLine(a); // <a><b y="zzzzzzz">x</b></a>
b.RemoveAttributes();
Trace.WriteLine(a); // <a><b>x</b></a>
b.SetValue(string.Empty); /* 空文字いれてもテキストノードは残る */
b.RemoveAttributes();
b.AddBeforeSelf(c);
Trace.WriteLine(b.IsEmpty); // False
Trace.WriteLine(a); // <a><c>m</c><b></b></a>
Trace.WriteLine(b.FirstNode == null ? "null" : ""); // null
/*
Remove系
Remove() - 自身を親ノードから削除
RemoveAll() - 自身の持つ子要素を全て削除
RemoveNodes() - 自身の持つ子ノードを全て削除
※SetElementValue("要素名", null) でも削除可能
*/
b.RemoveAll(); /* ここは RemoveNodes とやっても結果は同じ */
Trace.WriteLine(a); // <a><c>m</c><b /></a>
b.Add(c);
c.SetValue("xyz"); /* もう一方の c要素 には影響なし */
Trace.WriteLine(a); // <a><c>xyz</c><b><c>m</c></b></a>
名前空間
//
// 名前空間
//
XNamespace ns = "http://fernweh.jp/";
XElement xele;
/*
下の結果は
<ROOT xmlns="http://fernweh.jp/">
<A />
</ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XElement(xmlns + "A"));
Trace.WriteLine(xele);
/*
下の結果は
<ROOT xmlns="http://fernweh.jp/">
<A xmlns="" />
</ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XElement("A"));
Trace.WriteLine(xele);
/*
下の結果は
<ROOT xmlns="http://fernweh.jp/">
<A xmlns="" />
<A />
</ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XElement("A"),
new XElement(ns + "A"));
Trace.WriteLine(xele);
/*
下の結果は
<名前空間:ROOT xmlns:名前空間="http://fernweh.jp/">
<名前空間:A />
</名前空間:ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XAttribute(XNamespace.Xmlns + "名前空間", ns),
new XElement(xmlns + "A"));
Trace.WriteLine(xele);
/*
下の結果は
<名前空間:ROOT xmlns:名前空間="http://fernweh.jp/">
<A />
</名前空間:ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XAttribute(XNamespace.Xmlns + "名前空間", ns),
new XElement("A"));
Trace.WriteLine(xele);
/*
下の結果は
<名前空間:ROOT xmlns:名前空間="http://fernweh.jp/">
<A />
<名前空間:A />
</名前空間:ROOT>
*/
xele = new XElement(
ns + "ROOT",
new XAttribute(XNamespace.Xmlns + "名前空間", ns),
new XElement("A"),
new XElement(ns + "A"));
Trace.WriteLine(xele);
//
// 名前空間名の取得
// 名前空間名付きの要素を選択
//
XNamespace ns = "http://fernweh.jp/";
XElement xele = new XElement(
ns + "ROOT",
new XAttribute(XNamespace.Xmlns + "名前空間", ns),
new XElement("A", "first"),
new XElement(ns + "A", "second"));
XName xname = xele.Name;
XNamespace xnamespace = xname.Namespace;
Trace.WriteLine(xnamespace); // http://fernweh.jp
Trace.WriteLine(xele.Element("A").Value); // first
Trace.WriteLine(xele.Element(ns + "A").Value); // second
//
// 名前空間付きの属性を選択
//
XNamespace ns = "http://fernweh.jp/";
XElement xele = new XElement(
ns + "ROOT",
new XAttribute(XNamespace.Xmlns + "W", ns),
new XAttribute("a", "1st"),
new XAttribute(ns + "a", "2nd"));
Trace.WriteLine(xele); // <W:ROOT xmlns:W="http://fernweh.jp/" a="1st" W:a="2nd" />
Trace.WriteLine(xele.Attribute("a").Value); // 1st
Trace.WriteLine(xele.Attribute(ns + "a").Value); // 2nd
クローンとアタッチ
参考: Linq入門記-61 (LINQ to XML, 要素のクローンとアタッチ) - いろいろ備忘録日記
//
// 同じのをAddすると
// コピーされたものが後ろに追加される…
//
var a = new XElement("a");
var b = new XElement("b", "v");
a.Add(b);
a.Add(b);
a.Add(b);
(a.FirstNode as XElement).SetAttributeValue("id", "0");
(a.FirstNode.NextNode as XElement).SetAttributeValue("id", "1");
(a.LastNode as XElement).SetAttributeValue("id", "2");
b.Remove();
Trace.WriteLine(a); // <a><b id="1">v</b><b id="2">v</b></a>
XElement b1 = a.FirstNode as XElement;
b1.RemoveNodes();
Trace.WriteLine(a); // <a><b id="1" /><b id="2">v</b></a>
try
{
b.Remove(); // もう既にRemoveされているので…
}
catch (InvalidOperationException)
{
Trace.WriteLine("ココ通る");
}
Descendants
と Anscestors
Descendants
: 子孫要素を取得DescendantsAndSelf
: 子孫要素 + 自身 を取得DescendantNodes
: 子孫のノードを取得DescendantNodesAndSelf
: 子孫のノード + 自身 を取得Ancestors
: 先祖要素を取得AncestorsAndSelf
: 先祖要素 + 自身 を取得public static void Main() { string xml = @"<?xml version=""1.0"" encoding=""utf-8""?> <ROOT> <A /> <B /> <B> <C> <D>text-d</D> <E>text-e</E> <E><N x=""y"" /></E> </C> <F><G /></F> <H>text-h</H> テキスト </B> </ROOT>"; var doc = XDocument.Parse(xml); XElement c = doc.XPathSelectElement("ROOT/B/C"); // // Descendants で子Elementを取得(兄弟Elementは対象外) // 引数に null を与えると 空のIEnumerableが返ってくる // 属性名はヒットしない // WriteElementNames(c.Descendants()); // D, E, E, N WriteElementNames(c.Descendants("C")); // empty WriteElementNames(c.Descendants("D")); // D WriteElementNames(c.Descendants("E")); // E, E WriteElementNames(c.Descendants("N")); // N WriteElementNames(c.Descendants("X")); // empty WriteElementNames(c.Descendants(null)); // empty WriteElementNames(c.Descendants("x")); // empty // // DescendantsAndSelf は Descendants と違い自身を含む // WriteElementNames(c.DescendantsAndSelf()); // C, D, E, E, N WriteElementNames(c.DescendantsAndSelf("C")); // C WriteElementNames(c.DescendantsAndSelf("D")); // D WriteElementNames(c.DescendantsAndSelf("E")); // E, E WriteElementNames(c.DescendantsAndSelf("N")); // N WriteElementNames(c.DescendantsAndSelf("X")); // empty WriteElementNames(c.DescendantsAndSelf(null)); // empty // // Ancestors で子Elementを取得(兄弟Elementは対象外) // 引数に null を与えると 空のIEnumerableが返ってくる // WriteElementNames(c.Ancestors()); // B, ROOT WriteElementNames(c.Ancestors("C")); // empty WriteElementNames(c.Ancestors("B")); // B WriteElementNames(c.Ancestors("ROOT")); // ROOT WriteElementNames(c.Ancestors("A")); // empty WriteElementNames(c.Ancestors(null)); // empty // // AncestorsAndSelf は Ancestors と違い自身を含む // WriteElementNames(c.AncestorsAndSelf()); // C, B, ROOT WriteElementNames(c.AncestorsAndSelf("C")); // C WriteElementNames(c.AncestorsAndSelf("B")); // B WriteElementNames(c.AncestorsAndSelf("ROOT")); // ROOT WriteElementNames(c.AncestorsAndSelf("A")); // empty WriteElementNames(c.AncestorsAndSelf(null)); // empty // // DescendantNodes, DescendantNodesAndSelf で要素ではなくノードを列挙 // 引数は無し。 // // AncestorNodesは…もちろん存在しない。 // あっても Ancestors と同じだろうし。。 // WriteNodes(c.DescendantNodes()); // D, text-d, E, text-e, E, N WriteNodes(c.DescendantNodesAndSelf()); // C, D, text-d, E, text-e, E, N } private static void WriteElementNames(IEnumerable<XElement> elements) { if (elements.Count() == 0) { Trace.WriteLine("empty"); return; } var builder = new StringBuilder(); foreach (var xele in elements) { builder.Append(xele.Name + ", "); } builder.Length = builder.Length - ", ".Length; Trace.WriteLine(builder); } private static void WriteNodes(IEnumerable<XNode> nodes) { if (nodes.Count() == 0) { Trace.WriteLine("empty"); return; } var builder = new StringBuilder(); foreach (var xnode in nodes) { dynamic aNode = xnode; switch (xnode.NodeType) { case XmlNodeType.Element: builder.Append(aNode.Name + ", "); break; case XmlNodeType.Text: builder.Append(aNode.Value + ", "); break; default: throw new NotSupportedException(); break; } } builder.Length = builder.Length - ", ".Length; Trace.WriteLine(builder); }
Element
, Elements
, ElementsBeforeSelf
, ElementsAfterSelf
public class Sample
{
public static void Main()
{
string xml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<ROOT>
<A />
<B />
<B>
<C>
<D>text-d</D>
<E>text-e</E>
<E><N x=""y"" /></E>
</C>
<F><G /></F>
<H>text-h</H>
テキスト
</B>
</ROOT>";
var doc = XDocument.Parse(xml);
XElement c = doc.XPathSelectElement("ROOT/B/C");
//
// Elementメソッドで引数に指定した最初の子要素を取得
//
Trace.WriteLine(c.Element("A") == null); // True - 親はヒットしない
Trace.WriteLine(c.Element("C") == null); // True - 自身はヒットしない
Trace.WriteLine(c.Element("E").Value); // text-e
Trace.WriteLine(c.Element("N") == null); // True - 孫はヒットしない
//
// Elements で(引数に指定した)全ての子要素を取得
//
WriteElementNames(c.Elements()); // D, E, E
WriteElementNames(c.Elements("D")); // D
WriteElementNames(c.Elements("E")); // E, E
//
// ElementsBeforeSelf, ElementsAfterSelf で兄弟要素を取得
// 子孫親は含まれない。
//
WriteElementNames(c.ElementsBeforeSelf()); // empty
WriteElementNames(c.ElementsAfterSelf()); // F, H
WriteElementNames(c.ElementsAfterSelf("F")); // F
WriteElementNames(c.ElementsAfterSelf("G")); // empty
WriteElementNames(c.ElementsAfterSelf("H")); // H
//
// NodesBeforeSelf, NodesAfterSelf で兄弟ノードを取得
// 引数は取らない
//
WriteNodes(c.NodesBeforeSelf()); // empty
WriteNodes(c.NodesAfterSelf()); // F, H, テキスト
}
private static void WriteElementNames(IEnumerable<XElement> elements)
{
if (elements.Count() == 0)
{
Trace.WriteLine("empty");
return;
}
var builder = new StringBuilder();
foreach (var xele in elements)
{
builder.Append(xele.Name + ", ");
}
builder.Length = builder.Length - ", ".Length;
Trace.WriteLine(builder);
}
private static void WriteNodes(IEnumerable<XNode> nodes)
{
if (nodes.Count() == 0)
{
Trace.WriteLine("empty");
return;
}
var builder = new StringBuilder();
foreach (var xnode in nodes)
{
dynamic aNode = xnode;
switch (xnode.NodeType)
{
case XmlNodeType.Element:
builder.Append(aNode.Name + ", ");
break;
case XmlNodeType.Text:
builder.Append(aNode.Value + ", ");
break;
default:
throw new NotSupportedException();
break;
}
}
builder.Length = builder.Length - ", ".Length;
Trace.WriteLine(builder);
}
}
XPath
による要素の選択
//
// System.Xml.XPath
// の拡張メソッドが必要
//
var c = new XElement("c", "xxx");
var b = new XElement("b", c);
var a = new XElement("a", b);
Trace.WriteLine(a); // <a><b><c>xxx</c></b></a>
//
// XPathSelectElement でマッチする最初の要素を取得
// XPathSelectElements はマッチする全ての要素をIEnumerable<XElement> で返す
//
Trace.WriteLine(a.XPathSelectElement("b/c")); // <c>xxx</c>
Trace.WriteLine(a.XPathSelectElement("/b/c")); // <c>xxx</c>
Trace.WriteLine(a.XPathSelectElement("/a/b/c")); // (null) この辺り注意
Trace.WriteLine(a.XPathSelectElement("a/b/c")); // (null)
Trace.WriteLine(b.XPathSelectElement("b/c")); // (null)
Trace.WriteLine(b.XPathSelectElement("/b/c")); // <c>xxx</c>
Trace.WriteLine(b.XPathSelectElement("/a/b/c")); // (null)
Trace.WriteLine(b.XPathSelectElement("a/b/c")); // (null)
// XDeclarationはあってもなくても結果は同じだけど…
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), a);
Trace.WriteLine(doc); // <a><b><c>xxx</c></b></a>
Trace.WriteLine(a.XPathSelectElement("b/c")); // <c>xxx</c>
Trace.WriteLine(a.XPathSelectElement("/b/c")); // (null)
Trace.WriteLine(a.XPathSelectElement("/a/b/c")); // <c>xxx</c>
Trace.WriteLine(a.XPathSelectElement("a/b/c")); // (null)
Trace.WriteLine(b.XPathSelectElement("c")); // <c>xxx</c>
Trace.WriteLine(b.XPathSelectElement("/c")); // (null)
Trace.WriteLine(b.XPathSelectElement("/a/b/c")); // <c>xxx</c>
//
// XPathEvaluate は XPathSelectElements とほぼ同じ?
// 戻り値の型は object
//
IEnumerable result = b.XPathEvaluate("c") as IEnumerable;
Trace.WriteLine(result.GetType().Name); // <EvaluateIterator>d__0`1
foreach (var x in result)
{
Trace.WriteLine(x); // <c>xxx</c>
}
XNode#IsAfter
と XNode#IsTrue
var root = new XElement("ROOT");
var a = new XElement("A");
var b = new XElement("B");
var c = new XElement("C");
root.Add(a, b, c);
Trace.WriteLine(a.IsAfter(a)); // False
Trace.WriteLine(a.IsAfter(b)); // False
Trace.WriteLine(a.IsAfter(c)); // False
Trace.WriteLine(a.IsBefore(a)); // False
Trace.WriteLine(a.IsBefore(b)); // True
Trace.WriteLine(a.IsBefore(c)); // True
Trace.WriteLine(b.IsAfter(a)); // True
Trace.WriteLine(b.IsAfter(b)); // False
Trace.WriteLine(b.IsAfter(c)); // False
Trace.WriteLine(b.IsBefore(a)); // False
Trace.WriteLine(b.IsBefore(b)); // False
Trace.WriteLine(b.IsBefore(c)); // True
try
{
Trace.WriteLine(c.IsAfter(new XElement("d")));
}
catch (InvalidOperationException ex)
{
Trace.WriteLine(ex.Message); // 共通の先祖がありません。
}
Replace
系
null
を渡した時の挙動が嫌な感じ。。
var a = new XElement("A",
new XAttribute("x", "y"),
new XText("text"));
var r = new XElement("ROOT", a);
Trace.WriteLine(r); // <ROOT><A x="y">text</A></ROOT>
Trace.WriteLine(a.Parent.Name); // ROOT
//
// ReplaceNodes で 子ノードを置換
//
// 属性は置き換わらない。
// XAttribute を渡すと属性が追加されてノードが消える
//
a.ReplaceNodes(new XElement("B"));
Trace.WriteLine(a); // <A x="y"><B /></A>
a.ReplaceNodes(new XAttribute("z", "0"));
Trace.WriteLine(a); // <A x="y" z="0" />
try
{
a.ReplaceNodes(new XAttribute("z", "1"));
}
catch (InvalidOperationException ex)
{
Trace.WriteLine(ex.Message); // 属性が重複しています。
};
try
{
a.ReplaceNodes(new XAttribute("n", null));
}
catch (ArgumentNullException ex)
{
Trace.WriteLine(ex.Message); // 値を Null にすることはできません。 パラメーター名: value
}
a.Add(new XElement("G"));
a.ReplaceAttributes(new XAttribute("n", "m"));
Trace.WriteLine(a); // <A n="m"><G /></A>
//
// RelpaceAll は ReplaceNodes と違って属性も含めて置換
// で、ReplaceNodesと違って null を受け取れる。その場合は子要素を全削除
//
a.ReplaceAll(new XElement("D"));
Trace.WriteLine(a); // <A><D /><A>
a.ReplaceAll(null);
Trace.WriteLine(a); // <A />
//
// ReplaceWith で 自身を別要素に置換
// null を引数に渡すと自分自身を親からRemove(親がない場合はInvalidOperationException)
//
a.SetAttributeValue("c", "d");
Trace.WriteLine(a); // <A c="d" />
a.ReplaceWith(new XElement("B"), new XElement("C"));
Trace.WriteLine(r); // <ROOT><B /><C /></ROOT>
Trace.WriteLine(a.Parent == null ? "null" : ""); // null
r.ReplaceAll(a);
Trace.WriteLine(r); // <ROOT><A c="d"></ROOT>
a.ReplaceWith(null);
Trace.WriteLine(r); // <ROOT />
try
{
a.ReplaceWith(null);
}
catch (InvalidOperationException ex)
{
Trace.WriteLine(ex.Message); // 親がありません。
}
キャストで値を取得
参考: Linq入門記-87 (LINQ to XML, Tips, XElementとXAttributeをキャストして値取得) - いろいろ備忘録日記
キャスト演算子がオーバーライドされてて、Valueプロパティから取得した値を変換せずとも、キャストで直接変換可能。
var xele = new XElement("a", "true");
Trace.WriteLine((bool)xele); // True
Trace.WriteLine((string)xele); // true
try
{
Trace.WriteLine((int)xele);
}
catch (FormatException ex)
{
Trace.WriteLine(ex.Message); // 文字列入力の形式が正しくありません。
}