概要
C#でxmlテキストを作成する際に要素とコンテンツを動的に作成する方法を、自身の勉強を兼ねて記載してみました。
説明
xmlテキストにシリアライズするための階層構造になっているデータクラス群があるとします。
xmlの第2階層 (ルート要素の一つ下の子要素) 用のクラスは、紐づくクラスのプロパティの値から動的に要素と値を出力し、他のクラスは自身のプロパティをそのままシリアライズする形になります。
※第2階層のみ動的に要素名と値を出力するイメージです。
コード
前提として、シリアライズ後のxmlのイメージは以下になります。
<?xml version="1.0" encoding="utf-8"?>
<ROOT xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<HOGE>AAAA</HOGE>
<FUGA>BBBB</FUGA>
<PIYO>CCCC</PIYO>
<CONTENT_LIST>
<CONTENT>
<TEXT>1</TEXT>
</CONTENT>
<CONTENT>
<TEXT>2</TEXT>
</CONTENT>
<CONTENT>
<TEXT>3</TEXT>
</CONTENT>
</CONTENT_LIST>
</ROOT>
第2階層の<HOGE>
、<FUGA>
、<PIYO>
部分を動的に作成しているイメージです。
第2階層用のクラス
xml第2階層用のクラスです。
Layer2Items
というプロパティに、xmlの第2階層の要素名と値のまとまりが入っています。
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace Sample
{
[XmlRoot("ROOT")]
public class Layer2
{
[XmlAnyElement]
public List<Layer2Item> Layer2Items { get; set; }
[XmlElement("CONTENT_LIST")]
public ContentList ContentList { get; set; }
public void Set(List<Layer2Info> layer2Infos)
{
// 第2階層を作成する
var layer2Items = new List<Layer2Item>();
foreach (var layer2Info in layer2Infos.OrderBy(x => x.Index))
{
var item = new Layer2Item();
item.Set(layer2Info);
// プロパティに情報をセット
layer2Items.Add(item);
}
Layer2Items = layer2Items;
SetContentList(new ContentList());
}
private void SetContentList(ContentList contentList)
{
contentList.Set();
this.ContentList = contentList;
}
public string CreateXmlText()
{
// シリアライズ
string xmlText = Serialize();
// &のエスケープコードを消去し返却
return xmlText.Replace("amp;", "");
}
private string Serialize()
{
var serializer = new XmlSerializer(GetType());
var writer = new StringWriterUTF8();
serializer.Serialize(writer, this);
return writer.ToString();
}
private class StringWriterUTF8 : StringWriter
{
public override Encoding Encoding
{
get
{
return Encoding.UTF8;
}
}
}
}
}
説明
XmlAnyElement属性
予め定義していないXML要素を動的に処理するために使用するC#の属性です。
ここではxmlの第2階層の要素名や値を保持しているLayer2Items
プロパティに属性を付与しています。
Layer2クラスのプロパティに出力する情報クラス
Layer2
クラスのLayer2Items
プロパティの型となっているクラスです。
このクラス一つ分がxmlの第2階層の要素一つ分です。
Name
プロパティが要素名、Value
プロパティが要素の値です。
using System;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Sample
{
public class Layer2Item : IXmlSerializable
{
/// <summary>
/// 要素名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 値
/// </summary>
public string Value { get; set; }
/// <summary>
/// 1要素分のデータを取得する
/// </summary>
/// <returns>1要素分のデータ</returns>
public void Set(Layer2Info layer2Info)
{
Name = layer2Info.Name;
Value = layer2Info.Value;
}
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString(Name, Value);
}
}
}
説明
IXmlSerializableインターフェース
オブジェクトをXMLに変換する方法を完全に制御できるようにするインターフェースです。
IXmlSerializable
のWriteXml()
はオブジェクトの状態をxml形式で出力するためのメソッドです。このメソッドの中でXmlWriter
クラスのWriteElementString()
を使用し、要素名と値を指定してxml要素を生成します。
Layer2クラスのプロパティに出力する情報クラス一つ分
上記のLayer2Item
はこのデータクラスの値を使用しています。
このデータクラスには、ユーザー操作等でxml出力するための要素名や値があらかじめ格納されているイメージです。
Name
プロパティが要素名、Value
プロパティが要素の値です。
Index
プロパティを使用して連番とすることで、このデータクラス群 (Layer2Infos
) のどの順番でxml出力するかも操作できます。
public class Layer2Info
{
public int Index { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
CONTENT_LIST
用のクラス
xmlのCONTENT_LIST
に紐づくクラスです。
contents
プロパティにCONTENT
用のクラス群を保持しています。
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Sample
{
public class ContentList
{
[XmlElement("CONTENT")]
public List<Content> contents { get; set; }
public void Set()
{
var contents = new List<Content>();
// ここではnewしているが、ここは本来はxml出力する何かしらの値を取得する
var texts = new List<string>();
foreach (string text in texts)
{
var content = new Content();
content.Set(text);
contents.Add(content);
}
this.contents = contents;
}
}
}
CONTENT
用のクラス
xmlのCONTENT
に紐づくクラスです。
Text
プロパティがCONTENT
の子要素として出力する値です。
using System.Xml.Serialization;
namespace Sample
{
public class Content
{
[XmlElement("TEXT")]
public string Text { get; set; }
public void Set(string text)
{
Text = text;
}
}
}
xmlデータの出力
xmlデータの出力処理です。
先に出力するデータをセットしておき、その後にLayer2
クラスのCreateXmlText()
を呼出してシリアライズされたxmlのテキストを取得し、その後に任意の場所に出力しています。
// 出力するデータをセット
List<Layer2Info> layer2Infos = (第2階層に出力するLayer2Infoクラスのリスト);
var layer2 = new Layer2();
layer2.Set(layer2Infos);
// XMLテキストを作成
string xmlText = layer2.CreateXmlText();
// XMLを出力
using (var sw = new StreamWriter("出力先パス", false, new System.Text.UTF8Encoding(false)))
{
sw.Write(xmlText);
}
シリアライズ後のxmlデータ
最初にも記載しましたが、以下がシリアライズ後のxmlデータです。
<HOGE>
、<FUGA>
、<PIYO>
要素の要素名と値が、それぞれLayer2Item
クラスのName
プロパティやValue
プロパティに入っていた形です。
また、ContentList
クラスとContent
クラスはプロパティの値をそのまま出力できています。
動的に要素名を生成するLayer2
クラスと、属性名と値を指定してそのままシリアライズしているContentList
クラスとContent
クラスのように、組み合わせることもできます。
<?xml version="1.0" encoding="utf-8"?>
<ROOT xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<HOGE>AAAA</HOGE>
<FUGA>BBBB</FUGA>
<PIYO>CCCC</PIYO>
<CONTENT_LIST>
<CONTENT>
<TEXT>1</TEXT>
</CONTENT>
<CONTENT>
<TEXT>2</TEXT>
</CONTENT>
<CONTENT>
<TEXT>3</TEXT>
</CONTENT>
</CONTENT_LIST>
</ROOT>
終わりに
XmlAnyElement
属性やIXmlSerializable
インターフェースを使用すると、柔軟にxmlを作成できることがわかりました。