[C#] XDocumentを利用したXMLファイル操作まとめ(Linq、値の取得、値や属性の編集、削除)


はじめに

C#アプリケーションでは設定ファイルがXML形式担っていたりなど、XMLファイル操作は欠かせない技術の一つです。

そもそもXMLとは「Extensible Markup Language」の略であり、マークアップ言語の一つです。
詳細は省きますが、HTMLと同じようにタグを利用した言語となります。

今回は、このXMLファイルに記載されている情報の読み書きについてのまとめの備忘録です。

読み書きの方法はXDocumentやXMLDocumentなどいくつかありますが、今回はXDocumentを利用した方法をご紹介します。

XDocumentを利用したXML操作

まずは、利用するXMLファイルを下記とします。(sample.xml1)

<sample_root>
    <samples>
        <sample1 name="test1-1">testvalue1-1</sample1>
        <sample1 name="test1-2">testvalue1-2</sample1>
        <sample2>
            <sample2-1>testvalue2-1</sample2-1>
            <sample2-2>testvalue2-1</sample2-2>
        </sample2>
        <sample2>
            <sample2-1>testvalue2-1</sample2-1>
            <sample2-2>testvalue2-1</sample2-2>
        </sample2>
        <sample3>
            <sample3-1 name="subtest1">testvalue3</sample3-1>
        </sample3>
        <sample4>testvalue4</sample4>
    </samples>
</sample_root>

属性値およびタグ内の値の取得

xxxx=yyyy のような属性名=属性値の属性値を取得する方法とタグに囲まれた値の確認方法は下記の通りです。

まずは「sample1」の情報を取得します。

下記では、普通にループした状態で取得する方法と、属性値に対して出力する値を判定する方法を記載しています。

public static void Main(string[] args)
{
    var xmlfilePath = @"C:\Work\sample.xml";
    if (File.Exists(xmlfilePath) == false)
    {
        Console.WriteLine("file is not found.");
        return;
    }

    XDocument xd = XDocument.Load(xmlfilePath);
    XElement root = xd.Element("sample_root");
    IEnumerable<XElement> samplesEl = root.Elements("samples");
    foreach (var sampleEl in samplesEl)
    {
        // sample1 が二つあるのでElementsとして取得し、それぞれ属性値で判別する
        var sample1Els = sampleEl.Elements("sample1");
        Console.WriteLine("普通にループしてタグに囲まれた値を出力");
        foreach (var sample1El in sample1Els)
        {
            // 普通にループして出力
            Console.WriteLine(sample1El.Value);
        }
        Console.WriteLine();

        Console.WriteLine("普通にループしてnameに対する属性値を出力");
        foreach (var sample1El in sample1Els)
        {
            // 普通にループして出力
            Console.WriteLine(sample1El.Attribute("name").Value);
        }
        Console.WriteLine();

        Console.WriteLine("switchを利用して判定した結果を出力");
        foreach (var sample1El in sample1Els)
        {
            // 判定して出力
            var result = sample1El.Attribute("name").Value switch
            {
                "test1-1" => $"sample1の[test1-1]の値は「{sample1El.Value}」である",
                "test1-2" => $"sample1の[test1-2]の値は「{sample1El.Value}」である",
                _ => throw new InvalidOperationException()
            };
            Console.WriteLine(result);
        }
    }
    Console.ReadKey();
}

結果は下記の通りです

普通にループしてタグに囲まれた値を出力
testvalue1-1
testvalue1-2

普通にループしてnameに対する属性値を出力
test1-1
test1-2

switchを利用して判定した結果を出力
sample1の[test1-1]の値は「testvalue1-1」である
sample1の[test1-2]の値は「testvalue1-2」である

次は、「sample2」の情報を取得します。

sample2は複数のタグが存在し、それぞれのタグ配下にsample2-1/sample2-2が存在しています。

Console.WriteLine("sample2配下の値を取得する");
var sample2Els = sampleEl.Elements("sample2");
foreach (var sample2El in sample2Els)
{
    var sample2_1Els = sample2El.Elements("sample2-1");  
    foreach(var sample2_1El in sample2_1Els)
    {
        Console.WriteLine(sample2_1El.Value);
    }
    var sample2_2Els = sample2El.Elements("sample2-2");
    foreach (var sample2_2El in sample2_2Els)
    {
        Console.WriteLine(sample2_2El.Value);
    }
}
Console.WriteLine();

出力結果は下記の通りです。

sample2配下の値を取得する
testvalue2-1(1)
testvalue2-2(1)
testvalue2-1(2)
testvalue2-2(2)

次は「sample3」と「sample4」を取得します。

こちらは「sample1」と「sample2」を理解すれば簡単に取得できます。

Console.WriteLine("sample3配下の属性値と値を取得する");
var sample3Els = sampleEl.Elements("sample3");
foreach (var sample3El in sample3Els)
{
    var sample3_1El = sample3El.Element("sample3-1");
    Console.WriteLine(sample3_1El.Attribute("name").Value);
    Console.WriteLine(sample3_1El.Value);
}
Console.WriteLine();

Console.WriteLine("sample4の値を取得する");
var sample4El = sampleEl.Element("sample4");
Console.WriteLine(sample4El.Value);
Console.WriteLine();

出力結果は下記の通りです。

sample3配下の属性値と値を取得する
subtest1
testvalue3

sample4の値を取得する
testvalue4

XDocumentを利用する場合、構造を意識してコーディングしていく感じなので、ソースもみやすくなる反面、階層が深くなったり複雑なXMLの場合、foreachなどで回したりコードの見栄えがよろしく無くなってきますね。

Linq to XMLで読み込む

前述ではforeachで回して取得していますが、Linqを利用して取得することも可能です。

var sample1Value = sampleEl.Descendants("sample1").Where(s => s.Attribute("name").Value == "test1-1").FirstOrDefault();
Console.WriteLine(sample1Value.Value);

上記の出力結果は下記の通りです。

testvalue1-1

要素の編集

次に、XMLのデータを編集してみます。

今回は、要素の追加、属性値の編集、属性の追加の3つについてまとめておきます。

属性の追加

まずは属性を追加します。ここでは先ほど利用していたXMLのsample4に対して追加します。

var xmlfilePath = @"C:\Work\sample.xml";
XDocument xd = XDocument.Load(xmlfilePath);
XElement root = xd.Element("sample_root");
IEnumerable<XElement> samplesEl = root.Elements("samples");
foreach (var sampleEl in samplesEl)
{
    var sample4El = sampleEl.Element("sample4");
    sample4El.SetAttributeValue("name", "123");
}

出力結果は下記の通りです。

<sample_root>
  <samples>
    〜省略〜
    <sample4 name="123">testvalue4</sample4>
  </samples>
</sample_root>

問題なく name="123″が追加されています。

属性値の編集

先ほど追加した属性の値を編集します。

var xmlfilePath = @"C:\Work\sample.xml";
XDocument xd = XDocument.Load(xmlfilePath);
XElement root = xd.Element("sample_root");
IEnumerable<XElement> samplesEl = root.Elements("samples");
foreach (var sampleEl in samplesEl)
{
    var sample4El = sampleEl.Element("sample4");
    sample4El.SetAttributeValue("name", "456");
}
xd.Save(xmlfilePath);

name="456″に変更されていることがわかります。

<sample_root>
  <samples>
    〜省略〜
    <sample4 name="456">testvalue4</sample4>
  </samples>
</sample_root>

値の変更

値の変更はSetValueを利用します。

var xmlfilePath = @"C:\Work\sample.xml";
XDocument xd = XDocument.Load(xmlfilePath);
XElement root = xd.Element("sample_root");
IEnumerable<XElement> samplesEl = root.Elements("samples");
foreach (var sampleEl in samplesEl)
{
    var sample4El = sampleEl.Element("sample4");
    sample4El.SetValue("testvalue4(edit)");
}
xd.Save(xmlfilePath);

値が変更されました。

<sample_root>
  <samples>
    〜省略〜
    <sample4 name="456">testvalue4(edit)</sample4>
  </samples>
</sample_root>

要素の削除

sample4要素を削除します。

var xmlfilePath = @"C:\Work\sample.xml";
XDocument xd = XDocument.Load(xmlfilePath);
XElement root = xd.Element("sample_root");
IEnumerable<XElement> samplesEl = root.Elements("samples");
foreach (var sampleEl in samplesEl)
{
    var sample4El = sampleEl.Element("sample4");
    sample4El.Remove();
}
xd.Save(xmlfilePath);

値が削除されました。

<sample_root>
  <samples>
    〜省略〜
  </samples>
</sample_root>

さいごに

今回はXDocumentを利用したXMLの操作方法をまとめました。

XMLの操作としてはXMLDocumentを利用する方法もあります。

こちらについては次回説明したいと思います。