読者です 読者をやめる 読者になる 読者になる

C#でリストを持つエンティティからフラットな構成のXMLを作成する

.NET C#

こんにちは。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の扱いは多いのですが、属性を使用した例があまり見当たらなく結構戸惑いましたので参考になればと思います。

C#によるマルチコアのための非同期/並列処理プログラミング