[C# LINQ] XMLを操作する(LINQ to XML)

今回はLINQ to XMLについてです。

名前空間System.Xml.Linq をusingすると使えるようになります。
LINQ to XML を使うとこんな感じのことが出来ます。

  • ファイルやストリームからXMLを読み込む
  • ファイルやストリームにXMLを書きだす
  • LINQのメソッド(Whereなど)を使ってXMLツリーを操作する
スポンサーリンク

LINQ to XML で使用するクラス

System.Xml.Linqのクラスを紹介します。
詳細は公式ページを見てください。

XDocumentクラス

XML文書を表すクラス。
XDocumentには以下の要素を含めることができる。

  • 1つのXML宣言(XDeclaration)
  • 1つの要素(XElement)、これはルートノードを表す
  • 任意の数の処理命令(XProcessingInstruction)
  • 任意の数のコメント(XComment)
  • 1つの文書型宣言(XDocumentType)

LoadメソッドでファイルやストリームからXMLを読み込むことができる。
SaveメソッドでファイルやストリームへXMLを書きだすことができる。

XElementクラス

XMLの要素を表すクラス。
Addメソッドを使って子要素、属性の追加を行うことができる。
Removeメソッドを使って子要素、属性の削除を行うことができる。

LoadメソッドでファイルやストリームからXMLを読み込むことができる。こちらで読み込んだ場合、ルートノードがXElementオブジェクトに読み込まれます。

Nameプロパティで要素名を取得できる。

XAttributeクラス

XML属性を表すクラス。
Nameプロパティで属性名、Valueプロパティで値を取得できる。

XCommnetクラス

XMLコメントを表すクラス。

スポンサーリンク

XMLの作成と保存

XDocumentオブジェクトを作成して、Saveメソッドを実行するとXMLがファイルで出力されます。
XDocumentのコンストラクタは可変のパラメータで指定でき、XElementオブジェクトやXCommentオブジェクトを一気に詰め込むことができます。
XElementも同様にコンストラクタに可変パラメータを指定できます。

using System.Xml.Linq;
class Program
{
    public static void Main()
    {
        var xmlDoc = new XDocument(
            new XComment("test用xmlファイルです。"),
            new XElement("RootElement",
                new XElement("Element",
                    new XElement("Item1",
                        new XAttribute("attr1", "001"),
                        "Value1"
                    ),
                    new XElement("Item2",
                        new XAttribute("attr1", "002"),
                        new XAttribute("attr2", "002-1"),
                        "Value2"
                    )
                ),
                new XElement("Element",
                    new XElement("Item1",
                        new XAttribute("attr1", "003"),
                        "Value3"
                    ),
                    new XElement("Item2",
                        new XAttribute("attr1", "004"),
                        new XAttribute("attr2", "004-1"),
                        "Value4"
                    )
                )
            )
        );
        xmlDoc.Save("test1.xml");
    }
}

こんな感じのXMLファイルが出力されます。

<?xml version="1.0" encoding="utf-8"?>
<!--test用xmlファイルです。-->
<RootElement>
  <Element>
    <Item1 attr1="001">Value1</Item1>
    <Item2 attr1="002" attr2="002-1">Value2</Item2>
  </Element>
  <Element>
    <Item1 attr1="003">Value3</Item1>
    <Item2 attr1="004" attr2="004-1">Value4</Item2>
  </Element>
</RootElement>

また、オブジェクトを作った後でAddメソッドを使い要素を追加していくこともできます。

using System.Xml.Linq;

class Program
{
    public static void Main()
    {
        // 要素に追加する子要素
        var item = new XElement("Item1");
        item.Add(new XAttribute("attr1", "001"));
        item.SetValue("Value1");

        // ルートに追加する要素
        var elem = new XElement("Element");
        elem.Add(item);

        // ルートノード
        var rootElem = new XElement("RootElement");
        rootElem.Add(elem);

        // XML文書
        var xmlDoc = new XDocument();
        xmlDoc.Add(new XComment("test用xmlファイルです。"));
        xmlDoc.Add(rootElem);

        // ファイルに保存
        xmlDoc.Save("test2.xml");
    }
}

こんな感じのXMLファイルになります。

<?xml version="1.0" encoding="utf-8"?>
<!--test用xmlファイルです。-->
<RootElement>
  <Element>
    <Item1 attr1="001">Value1</Item1>
  </Element>
</RootElement>
スポンサーリンク

XMLの読み込み

XDocumentクラスのLoadメソッドを使うとファイルからXMLを読み込むことができます。

上記「XMLの作成と保存」で作ったファイルを読み込んでみます。

using System.Xml.Linq;
class Program
{
    public static void Main()
    {
        var doc = XDocument.Load("test1.xml");
    }
}

読み込みはおどろきの1行で出来てしまいます。
次は読み込んだXMLからデータを抽出したり、編集したりです。

スポンサーリンク

XMLデータを操作する

XDocumentに読み込んだXMLにアクセスするには以下のメソッドを使います。
戻り値がIEnumerable<T>型で返ってくるものにはLINQのメソッド(Select、Whereなど)を使用することができます。using System.Linq;を忘れずに。

以下のメソッドはXDocumentXElementの2つのクラスが持っているメソッドです。

メソッド名説明戻り値
Nodes子ノード(XNode)のコレクションをドキュメント順に返す
ノードには要素ノード(XElement)、
コメントノード(XComment)などが含まれる
IEnumerable<XNode>
Element引数で指定した名前の子要素ノード(XElement)を返す
該当する要素ノードが複数ある場合、ドキュメント順の先頭のノードを返す
該当する要素ノードが見つからない場合はnullが返ってくる
XElement
Elements子要素(XElement)のコレクションをドキュメント順に返すIEnumerable<XElement>
DescendantNodes子孫ノードのコレクションをドキュメント順に返すIEnumerable<XNode>
Descendants子孫要素のコレクションをドキュメント順に返すIEnumerable<XElement>
Ancestors先祖要素のコレクションを返すIEnumerable<XElement>

XElementは以下のメソッドも持っています。

メソッド名説明戻り値
Attribute要素から引数で指定した名前の属性を取得するXAttribute
Attributes要素の持つ属性のコレクションを返すIEnumerable<XAttribute>

XDocumentRootプロパティを使うとXML文書のルート要素(XElement)を取得することが出来ます。

こんなXMLファイルを読み込んでみます。

<?xml version="1.0" encoding="utf-8"?>
<!--test用xmlファイルです。-->
<Root>
  <Child>
    <GrandChild attr1="001">Value1</GrandChild>
    <GrandChild attr1="002">Value2</GrandChild>
  </Child>
  <Child>
    <GrandChild attr1="003">Value3</GrandChild>
    <GrandChild attr1="004">Value4</GrandChild>
  </Child>
</Root>
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
    public static void Main()
    {
        // XMLファイルを読み込む
        var doc = XDocument.Load("test3.xml");

        // 子ノードを全て取得
        // ↓にはコメントノード、要素ノード(RootElement)が入る
        var nodes = doc.Nodes();
        Console.WriteLine("docが持っているノードの種類は...");
        Console.WriteLine(string.Join("\n", nodes.Select(n => $"NodeType = {n.NodeType}")));

        // 子ノードからコメントノードを抽出
        var commnets = nodes.OfType<XComment>();
        // 取得したコメントを表示
        Console.WriteLine("\nコメントを表示");
        Console.WriteLine(string.Join("\n", commnets.Select(c => c.Value)));

        // 要素Rootを取得
        var rootElem = doc.Element("Root");

        // 要素Rootが持つ子要素をすべて取得
        var elements = rootElem.Elements();
        Console.WriteLine("\nRootElementが持つ子要素の名前は...");
        Console.WriteLine(string.Join("\n", elements.Select(e => e.Name)));

        foreach (var child in elements) {
            // Child要素が持つの全ての子要素(GrandChild)を取得
            var grandChildren = child.Elements();
            foreach (var gc in grandChildren) {
                // GrandChild要素が持つ属性、テキストノードを表示
                var attr = gc.Attribute("attr1");
                Console.WriteLine($"Name={gc.Name}, Value={gc.Value}, Attribute=[Name={attr.Name}, Value={attr.Value}]");
            }
        }

    }
}

処理結果はこんな感じです。

docが持っているノードの種類は...
NodeType = Comment
NodeType = Element

コメントを表示
test用xmlファイルです。

RootElementが持つ子要素の名前は...
Child
Child
Name=GrandChild, Value=Value1, Attribute=[Name=attr1, Value=001]
Name=GrandChild, Value=Value2, Attribute=[Name=attr1, Value=002]
Name=GrandChild, Value=Value3, Attribute=[Name=attr1, Value=003]
Name=GrandChild, Value=Value4, Attribute=[Name=attr1, Value=004]
スポンサーリンク

ノードや要素を編集する

編集したいノードや要素をLINQなどで取得してそのオブジェクトのメソッドで編集を行う感じです。

読み込むXMLは上と同じでこんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<!--test用xmlファイルです。-->
<Root>
  <Child>
    <GrandChild attr1="001">Value1</GrandChild>
    <GrandChild attr1="002">Value2</GrandChild>
  </Child>
  <Child>
    <GrandChild attr1="003">Value3</GrandChild>
    <GrandChild attr1="004">Value4</GrandChild>
  </Child>
</Root>
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
    public static void Main()
    {
        // XMLファイルを読み込む
        var doc = XDocument.Load("test3.xml");

        // Attribute(attr1)の値が003の要素を取得
        var element = doc.Root.Elements().Elements().FirstOrDefault(e => e.Attribute("attr1")?.Value == "003");
        // 属性の変更
        element.SetAttributeValue("attr1", "999");
        // 属性の追加
        element.SetAttributeValue("attr2", "abc");
        // 値を変更
        element.SetValue(10);
        // 要素を追加
        element.Add(new XElement("AddElement1"));
        // 先頭に要素を追加
        element.AddFirst(new XElement("AddElement2"));

        // Root要素の先頭の子要素を取得
        var lastElement = doc.Root.Elements().First();
        // 要素を削除
        lastElement.Remove();
        
        // ファイルに保存
        doc.Save("test4.xml");
    }
}

処理を実行して出力されたファイルはこんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<!--test用xmlファイルです。-->
<Root>
  <Child>
    <GrandChild attr1="999" attr2="abc">
      <AddElement2 />10<AddElement1 /></GrandChild>
    <GrandChild attr1="004">Value4</GrandChild>
  </Child>
</Root>

C# プログラミング講座に戻る

コメント

タイトルとURLをコピーしました