C# で XML ファイルを作成するときの比較(XmlDocument, XDocument, ExDoc)

「ツイートキャッチ★★★」で xml 形式でファイルに出力するために、ExDoc を拡張している途中です。ExDoc の主旨として「ユーザーインターフェースを優先する」という項目があるので(勝手に自分で付けたw)、ユーザーから使いやすいコーディングの仕方に合わせて拡張していきます。ユーザーってのは、「コーディングをするプログラマ」ってことですね、新しい UIDD(User Interface Developement Driven)の一環です(というのも、勝手に自分で付けたw)

と前置きはそれくらいにして、コードを晒しておきます。
TwiCatchStar の内部データを直接 xml に書き出します。

■XmlDocument を使う場合

通常の XmlDocument を使う場合です。まあ、旧来の DOM インターフェースを知っていれば作れるので、java などの他言語から入った人にはいいのでしょうが、結構手間です。

// XmlDocument を使う場合
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("TwiCatchStar");
root.SetAttribute("version", APPVERSION);
root.SetAttribute("created_at", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
doc.AppendChild(root);
XmlElement user = doc.CreateElement("user");
root.AppendChild(user);
user.AppendChild(doc.CreateElement("screen_name")).InnerText= username;
user.AppendChild(doc.CreateElement("profile_image_url")).InnerText= profile_image_url;
XmlElement statuses = doc.CreateElement("statuses");
statuses.SetAttribute("type", "array");
root.AppendChild(statuses);
foreach (Tweet twi in items)
{
	XmlElement status = doc.CreateElement("status");
	statuses.AppendChild(status);
	status.AppendChild(doc.CreateElement("id")).InnerText= twi.ID;
	status.AppendChild(doc.CreateElement("text")).InnerText= twi.text;
	status.AppendChild(doc.CreateElement("created_at")).InnerText= twi.CreatedAt.ToString("yyyy/MM/dd HH:mm:ss");
}

■LINQ to XML を使う場合

今後、.NET 言語でデータストレージ絡み(XML形式も含む)のは、一括して LINQ が推奨、ってことになるんでしょうが、LINQ to XML っていまいち使いづらいんですよね。それでも、XmlDocument よりは直感的かも。

// LINQ to XML を使う場合
XDocument doc = new XDocument();
XElement root = new XElement("TwiCatchStar");
doc.Add(root);
root.SetAttributeValue("version", APPVERSION);
root.SetAttributeValue("created_at", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
XElement user = new XElement("user");
root.Add(user);
user.Add(new XElement("screen_name", username));
user.Add(new XElement("profile_image_url", profile_image_url));
XElement statuses = new XElement("statuses");
statuses.SetAttributeValue("type", "array");
foreach (Tweet twi in items)
{
	XElement status = new XElement("status");
	statuses.Add(status);
	status.Add(new XElement("id", twi.ID));
	status.Add(new XElement("text", twi.text));
	status.Add(new XElement("created_at", twi.CreatedAt.ToString("yyyy/MM/dd HH:mm:ss")));
}

■ExDoc を使う場合

ExDoc は「もう LINQ to XML はいらない」を主旨にして作っているので、+= 演算子や []演算子を適当にオーバーライドして作成できるようにします。
本来ならば、CreateElement とか AppendElement という「名前」を使わずにやりたいのですが、ちょっと模索中。

EXDocument doc = new EXDocument("TwiCatchStar");
doc.Root["version"] = APPVERSION;
doc.Root["created_at"] = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
EXElement user = doc.Root.AppendElement("user");
user += doc.CreateElement("screen_name", username);
user += doc.CreateElement("profile_image_url", profile_image_url);
EXElement statuses = doc.Root.AppendElement("statuses");
statuses["type"] = "array";
foreach (Tweet twi in items)
{
	EXElement status = statuses.AppendElement("status");
	status += doc.CreateElement("id", twi.ID);
	status += doc.CreateElement("text", twi.text);
	status += doc.CreateElement("created_at", twi.CreatedAt.ToString("yyyy/MM/dd HH:mm:ss"));
}

LINQ to XML では、Add メソッド内で要素を作成していますが、実は適当な拡張メソッドを作れば AppendElement のような形でアクセスが可能なので、ExDoc の優位性というのはこの場合はあまりありません。

■VB で作る場合

実は、VB で作るのが一番直感的なんですよね。XML 用の拡張文法が VB に備わっているので、これを使うと…

'''
''' コレクション
'''
'''
Public _tweets As List(Of Tweet)
'''
''' エンティティクラス
'''
'''
Public Class Tweet
	Public Property ID As String
	Public Property Text As String
	Public Property Create_at As Date
End Class

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
	Dim APPVERSION = "0.3"
	Dim username = "moonmile"
	Dim profile_image_url = ""

	Dim root As XElement =
	 <TwiCatchStar version=<%= APPVERSION %> created_at=<%= Date.Now.ToString("yyyy/MM/dd HH:mm:ss") %>>
	 </TwiCatchStar>

	Dim user As XElement =
	 <user>
		 <screen_name><%= username %></screen_name>
		 <profile_image_url><%= profile_image_url %></profile_image_url>
	 </user>

	Dim statuses =
	 <statues>
		 <%= From t In _tweets Select
			 <status>
				 <id><%= t.ID %></id>
				 <text><%= t.Text %></text>
				 <create_at><%= t.Create_at.ToString("yyyy/MM/dd HH:mm:ss") %></create_at>
			 </status> %>
	 </statues>

	root.Add(user)
	root.Add(statuses)

	Me.TextBox1.Text = root.ToString()

End Sub

のように、なんて直感的に書けるんでしょうッ!!! まるで、ASP.NET の埋め込みか、PHP のように記述ができます。これには、ExDoc も脱帽 orz
一見、VB のほうが行数が長いので非効率に見えるのですが、保守性は抜群ですよね。一瞥しただけで変更が可能です。このために VB を使うのもいいかも(違

2012/07/08 追記

Egtra さんより教えて貰った方法で、LINQ to XML を書き直すとこんな感じ。
この記事を書いたときに、XElement に子のコレクションをどう入れれば分からなかったのですが、items.Select な感じでコレクションを渡すと、自動的に子に配列として付け加わるのですね。なるほど。

XDocument doc = new XDocument(
	new XElement("TwiCatchStar",
		new XAttribute("version", APPVERSION),
		new XAttribute("created_at", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")),
		new XElement("user",
			new XElement("screen_name", username),
			new XElement("profile_image_url", profile_image_url)),
		new XElement("statuses",
			new XAttribute("type", "array"),
			items.Select(twi =>
				new XElement("status",
					new XElement("id", twi.ID),
					new XElement("text", twi.Text),
					new XElement("created_at", twi.CreatedAt.ToString("yyyy/MM/dd HH:mm:ss")))))));

実は以前、XElement を扱った時「これってDOMに準拠していない」(DocumentからNodeを作成しない)という点でちょっと嫌っていたのですが、こういう書き方ができると、VB のように XML の構造通りに書けるからいいですよね。
ExDoc も doc.CreateElement のようにせずに、やっぱり doc += new EXElement(…) とか、doc.Append( new EXElement(…), new EXElemnet(…)) のように羅列できるように改造しようかな。

カテゴリー: UIDD, C#, VB パーマリンク

C# で XML ファイルを作成するときの比較(XmlDocument, XDocument, ExDoc) への2件のフィードバック

  1. Egtra のコメント:

    LINQ to XMLには関数型構築はどうでしょうか。VB .NETにはかなわないですが、XMLの構造を式で表現することを目指した書き方です。
    XDocument doc = new XDocument(
    new XElement(“TwiCatchStar”,
    new XAttribute(“version”, APPVERSION),
    new XAttribute(“created_at”, DateTime.Now.ToString(“yyyy/MM/dd HH:mm:ss”)),
    new XElement(“user”,
    new XElement(“screen_name”, username),
    new XElement(“profile_image_url”, profile_image_url)),
    new XElement(“statuses”,
    new XAttribute(“type”, “array”),
    items.Select(twi =>
    new XElement(“status”,
    new XElement(“id”, twi.ID),
    new XElement(“text”, twi.text),
    new XElement(“created_at”, twi.CreatedAt.ToString(“yyyy/MM/dd HH:mm:ss”)))))));

    • masuda のコメント:

      コメントありがとうございます。なるほど、XElement に複数の子要素を追加するときはそうするのか( items.Select のところ)。この部分がよくわからなくて、避けていたんですが、これができると XElement は使いやすそうですね。
      記事のほうに追記しました。

コメントは停止中です。