こんにちは。beaglesoftの真鍋です。
XMLを作成するときに、Listなどのフィールドを保持するクラスからXMLを生成するときに、要素が二重になってしまうことがありちょっと調べたことがありますのでまとめます。
サンプルの全体はGitHubからダウンロードできます
yoichiro-manabe/SampleApps: ASP.NET MVC5でアクションメソッドの処理結果をXMLで返却するサンプル
作成したエンティティ
XMLを作成するために以下のエンティティを作成しました。
XMLのルートとなるクラス
このクラスはリストを保持しています。リストには複数要素を保持することを明確にするためXmlArray
属性を設定しています。
// SampleRoot.cs using System.Collections.Generic; using System.Xml.Serialization; namespace SampleApps.Models { [XmlRoot(ElementName = "samples")] public class SampleRoot { [XmlArray("sample")] public List<Sample> Samples { get; set; } = new List<Sample>(); } }
要素として追加されるクラス
こちらはSampleRoot
が保持する要素に対応するクラスです。今回はId
を属性としてName
は値として出力することにしました。
//Sample.cs using System.Xml.Serialization; namespace SampleApps.Models { public class Sample { [XmlAttribute("id")] public int Id { get; set; } [XmlText] public string Name { get; set; } } }
生成したいXML
もともと出力を想定しているXMLは以下のようになります。
<?xml version="1.0" encoding="utf-8"?> <samples> <sample id="1">サンプル1</sample> <sample id="2">サンプル2</sample> </samples>
生成されたXML
ところが、XMLを生成したところ次のようなXMLとなりました。
<?xml version="1.0" encoding="utf-8"?> <samples> <sample> <Sample id="1">サンプル1</Sample> <Sample id="2">サンプル2</Sample> </sample> </samples>
samples
要素の中にsample
要素が設定されてしまっています。
対応方法
いろいろと調べたところ、問題はSampleRoot
クラスに設定している[XmlArray("sample")]
でした。複数の要素を管理するのでこの属性を指定したのですが、間違いでした。
属性を使用した XML シリアル化の制御.aspx)
ここでは「配列のシリアル化の制御」で失敗した事象が説明されています。どうやらXmlArray
属性は繰り返し要素の名称を変更するために利用するようです。
一方で今回求めていた内容は「要素のシーケンスとしての配列のシリアル化」に当たります。
次の例に示すように、配列を返すフィールドにXmlElementAttributeを適用して、配列をXML要素のフラットなシーケンスとしてシリアル化することもできます。
とあるように、配列をXML要素としてはフラットに表現したいためこちらが正しい利用法となります。
XMLのルートとなるクラスの最終形
using System.Collections.Generic; using System.Xml.Serialization; namespace SampleApps.Models { [XmlRoot(ElementName = "samples")] public class SampleRoot { [XmlElement("sample")] public List<Sample> Samples { get; set; } = new List<Sample>(); } }
まとめ
とりあえず、XMLに関する情報があまりなくよりどころが少ないですが、シリアル化されたXMLと対応するソースコードが掲載されているMSDNはありがたいです。SAXとかDOMの扱いは多いのですが、属性を使用した例があまり見当たらなく結構戸惑いましたので参考になればと思います。