C# で Key、Value な コレクションを XML にしてみた

元ネタ:
GroovyのMarkupBuilderで再起的な構造のXMLを生成する - No Programming, No Life
GroovyでKey、ValueをXMLにクールに出力する-keyValueXml.groovy- - Togetter


Groovy では乗り遅れた/書けない ので C# の勉強も兼ねて書いてみた。

本体

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

public class Class1
{
  public string ToXml(Dictionary<string, object> data)
  {
    var root = new XElement("langs", data.Select(
      e => new XElement(e.Key, e.Value.CreateValue())
    ));
    return root.ToString();
  }
}
internal static class Extensions
{
  public static object CreateValue(this object source)
  {
    if (source is Dictionary<string, object>)
    {
      return ((Dictionary<string, object>)source).Select(e => new XElement(e.Key, e.Value.CreateValue()));
    }
    return source.ToString();
  }
}

テスト

using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var data = new Dictionary<string, object>() {
      { "key1", "value1" }, 
      { "key2", "value2" }, 
      { "key3", new Dictionary<string, object>() { 
        { "key3-1", "value3-1" }, 
        { "key3-2", "value3-2" }, 
      }}, 
    };

    var instance = new Class1();
    string actual = instance.ToXml(data);

    string expected = @"<langs>
  <key1>value1</key1>
  <key2>value2</key2>
  <key3>
    <key3-1>value3-1</key3-1>
    <key3-2>value3-2</key3-2>
  </key3>
</langs>";

    Assert.AreEqual(expected, actual);
  }
}

LINQ to XML ひゃっはー!
※ここをパクッた。方法 : LINQ to XML を使用してディクショナリを操作する
拡張メソッド CreateValue が少々ダサいけど。。ここまでは LINQ to XML があるので楽に書ける。


で togetter を読み進めると色んな Groovy コードが出てきた。
id:kiy0taka さん作 map2xml.groovy
Marshaller は as で型変換する際に上手い事やってくれる内容を定義してるのかな?
キャスト で良いなら C# でも「一応」書ける。(Groovy よりは不自然…)

本体 (拡張メソッド CreateValue は前回と一緒なので省略)

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

public class XElementEx : XElement
{
  public XElementEx(XName name, object content) : base(name, content) { }
  public static explicit operator XElementEx(Dictionary<string, object> target)
  {
    return new XElementEx("langs", target.Select(
      e => new XElement(e.Key, e.Value.CreateValue())
    ));
  }
}

テスト

using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod2()
  {
    var data = new Dictionary<string, object>() {
      { "key1", "value1" }, 
      { "key2", "value2" }, 
      { "key3", new Dictionary<string, object>() { 
        { "key3-1", "value3-1" }, 
        { "key3-2", "value3-2" }, 
      }}, 
    };
    XElementEx xml = (XElementEx)data;
    string actual = xml.ToString();
    string expected = @"<langs>
  <key1>value1</key1>
  <key2>value2</key2>
  <key3>
    <key3-1>value3-1</key3-1>
    <key3-2>value3-2</key3-2>
  </key3>
</langs>";
    Assert.AreEqual(expected, actual);
  }
}

explicit (C# リファレンス) 使って XElement を継承した型にキャスト出来るようにしてみた。implicit (C# リファレンス) にすると、明示的なキャストも不要(暗黙の型変換)になる。
ただ、変換演算子の使用 (C# プログラミング ガイド)(explicit や impicit) を使うケースは、上の様な時では無いので、仕事じゃ書かないな…。*1


次に id:uehaj さん作の convert map to xml
短くてシンプル、mkp.yield ってのが何か分からなかった。mkp ってのは、MarkupBuilder の中で使える特殊な物なのかな?
MarkupBuilder (Groovy 2.2.1)


MarkupBuilder が便利そうですねー。

*1:型安全のために Decimal をラップしただけの 金額 クラスとかを作ったりした場合、変換演算子を用意する利点がある?