【C#】xmlデータをデシリアライズする際のデータクラスや属性の定義方法

# 概要
C#でxmlデータをデシリアライズする際の、データクラスの構成や属性定義についてのサンプルをまとめました。

# 属性名について
本稿で登場する、xmlのデシリアライズを行う際に必要な属性についてまとめました。

### XmlRoot
XMLのルート要素名を指定できます。
クラスに適用します。

### XmlElement
プロパティやフィールドの名前をXML要素にマップできます。

### XmlAttribute
プロパティやフィールドの値をXML属性としてマップできます。

### XmlArray
配列やリストをXML配列としてマップする際に使用します。

### XmlArrayItem
XML配列内の各アイテムを指定する際に使用します。
XmlArray属性と組み合わせて使用します。

### XmlInclude
XmlSerializerが派生クラスを認識できるようにするために使用されます。

### XmlText
XMLの要素内のコンテンツテキストを指定する際に使用します。

# デシリアライズの例

## パターン1
### XMLデータの構成の説明

| 要素名 | 説明 |
| ---- | ---- |
| Sample | ルート要素 |
| HogeInfos<br>FugaInfos<br>PiyoInfos | 要素数は必ず1つのみ |
| ItemInfo | HogeInfos、FugaInfos、PiyoInfosの子要素<br>要素数は1つ以上 |

### xmlデータ

```xml
<Sample>
  <HogeInfos>
    <ItemInfo Keyword="keyword1" Name="Name1" Value="Value1"/>
    <ItemInfo Keyword="keyword2" Name="Name2" Value="Value2"/>
  </HogeInfos>
  <FugaInfos>
    <ItemInfo Keyword="keyword3" Name="Name3" Value="Value3"/>
  </FugaInfos>
  <PiyoInfos>
    <ItemInfo Keyword="keyword4" Name="Name4" Value="Value4"/>
    <ItemInfo Keyword="keyword5" Name="Name5" Value="Value5"/>
    <ItemInfo Keyword="keyword6" Name="Name6" Value="Value6"/>
  </PiyoInfos>
</Sample>
```
### データクラスの定義
#### 定義例1
```C#
    [XmlRoot("Sample")]
    public class Sample
    {
        [XmlArray("HogeInfos")]
        [XmlArrayItem("ItemInfo")]
        public List<ItemInfo> HogeInfos { get; set; }

        [XmlArray("FugaInfos")]
        [XmlArrayItem("ItemInfo")]
        public List<ItemInfo> FugaInfos { get; set; }

        [XmlArray("PiyoInfos")]
        [XmlArrayItem("ItemInfo")]
        public List<ItemInfo> PiyoInfos { get; set; }
    }

    public class ItemInfo
    {
        [XmlAttribute("Keyword")]
        public string Keyword { get; set; }

        [XmlAttribute("Name")]
        public string Name { get; set; }

        [XmlAttribute("Value")]
        public string Value { get; set; }
    }
```
- `XmlRoot`属性に定義した名前はクラス名と同じなら、属性自体の記載を省けます。
- `XmlArray`属性はプロパティ名が、`XmlArrayItem`は対応するクラス名が同じなら属性自体の記載を省けます。
- `XmlAttribute`属性は記載を省くとプロパティの値がnullになってしまうので、記載する必要があります。

#### 定義例2

```C#
    [XmlRoot("Sample")]
    public class Sample
    {
        [XmlElement("HogeInfos")]
        public HogeInfo HogeInfos { get; set; }

        [XmlElement("FugaInfos")]
        public FugaInfo FugaInfos { get; set; }

        [XmlElement("PiyoInfos")]
        public PiyoInfo PiyoInfos { get; set; }
    }

    public class HogeInfo
    {
        [XmlElement("ItemInfo")]
        public List<ItemInfo> ItemInfos { get; set; }
    }

    public class FugaInfo
    {
        [XmlElement("ItemInfo")]
        public List<ItemInfo> ItemInfos { get; set; }
    }

    public class PiyoInfo
    {
        [XmlElement("ItemInfo")]
        public List<ItemInfo> ItemInfos { get; set; }
    }
    
    public class ItemInfo
    {
        [XmlAttribute("Keyword")]
        public string Keyword { get; set; }

        [XmlAttribute("Name")]
        public string Name { get; set; }

        [XmlAttribute("Value")]
        public string Value { get; set; }
    }
```
- `Sample`クラス内の`XmlElement`属性は、定義した名前とプロパティ名が同じなら、属性自体の記載を省けます。
- `HogeInfo`、`FugaInfo`、`PiyoInfo`クラス内の`XmlElement`属性は、定義した名前とリストとなるクラス名が同じなら省けます。

### デシリアライズ方法

```C#
        private Sample ReadXml(string path)
        {
            try
            {
                if (!File.Exists(path))
                {
                    // エラー処理
                }

                var xml = XElement.Load(path);
                string xmlText = xml.ToString();

                var serializer = new XmlSerializer(typeof(Sample));
                var deserializedData = (Sample)serializer.Deserialize(new StringReader(xmlText));

                return deserializedData;
            }
            catch (Exception ex)
            {
                // エラー処理等
            }
        }
```

#### 備考
データクラスの定義例1と2でデシリアライズ後のデータクラスの階層の構成は違いがありますが、どちらもデシリアライズできます。

## パターン2

### XMLデータの構成の説明
| 要素名 | 説明 |
| ---- | ---- |
| Sample | ルート要素 |
| HogeData<br>FugaData | 要素数は必ず1つのみ |
| Targets | HogeData、FugaDataの子要素<br>要素数は1つ以上 |
| Keyword | Targetsの子要素<br>要素数は1つ以上 |

### xmlデータ

```xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Sample version="">
	<HogeData>
		<Targets Value="あたい">
			<Keyword Text="もじ">AA</Keyword>
			<Keyword Text="モジ">BB</Keyword>
		</Targets>
		<Targets Value="アタイ">
			<Keyword Text="文字">CC</Keyword>
		</Targets>
	</HogeData>
	<FugaData>
		<Targets Value="値">
			<Keyword Text="moji">DD</Keyword>
		</Targets>
	</FugaData>
</Sample>
```

### データクラス
#### 定義例1
```C#
    [XmlRoot("Sample")]
    public class Sample
    {
        [XmlElement("HogeData")]
        public HogeData hogeData { get; set; }

        [XmlElement("FugaData")]
        public FugaData FugaData { get; set; }
    }

    public class DataTypeBase
    {
        [XmlElement("Targets")]
        public List<Targets> Targets { get; set; }
    }

    public class HogeData : DataTypeBase
    {
    }

    public class FugaData : DataTypeBase
    {
    }

    public class Targets
    {
        [XmlAttribute("Value")]
        public string Value { get; set; }

        [XmlElement("Keyword")]
        public List<Keyword> Keywords { get; set; }
    }

    public class Keyword
    {
        [XmlAttribute("Text")]
        public string Text { get; set; }

        [XmlText]
        public string Value { get; set; }
    }
```
#### 定義例2
```C#
    [XmlRoot("Sample")]
    public class Sample
    {
        [XmlElement("HogeData")]
        public HogeData hogeData { get; set; }

        [XmlElement("FugaData")]
        public FugaData FugaData { get; set; }
    }

    [XmlInclude(typeof(HogeData))]
    [XmlInclude(typeof(FugaData))]
    public abstract class DataTypeBase
    {
        [XmlElement("Targets")]
        public List<Targets> Targets { get; set; }
    }

    public class HogeData : DataTypeBase
    {
    }

    public class FugaData : DataTypeBase
    {
    }

    [XmlInclude(typeof(Targets))]
    public class Targets
    {
        [XmlAttribute("Value")]
        public string Value { get; set; }

        [XmlElement("Keyword")]
        public List<Keyword> Keywords { get; set; }
    }

    public class Keyword
    {
        [XmlAttribute("Text")]
        public string Text { get; set; }

        [XmlText]
        public string Value { get; set; }
    }
```


### デシリアライズ方法
[こちら](#デシリアライズ方法)と同様。

## 補足
### シリアライズについて
デシリアライズ後のクラスを使用して、以下のようにシリアライズもできます。

```C#
    var xml = XElement.Load(path);
    string xmlText = xml.ToString();

    // デシリアライズ
    var serializer = new XmlSerializer(typeof(Sample));
    var deserializedData = (Sample)serializer.Deserialize(new StringReader(xmlText));

    // シリアライズ
    serializer = new XmlSerializer(typeof(Sample));
    var writer = new StringWriter();
    serializer.Serialize(writer, deserializedData);
    xmlText = writer.ToString();  // xml形式の文字列が取得できる (デシリアライズ前と同様の文字列)
```

# 終わりに
それぞれの属性の細かな動きはまだ学習中ですが、単純なxmlのデシリアライズの際に役に立てればと思い記載しました。