【.NET】XMLファイルを DataSet に読み込み

# はじめに
同僚がデータ修正ツールを作成する上で、ASP.NETでドロップボックスのリストを作成するサンプルを作成していた。
リストを作成する上で対象データをXMLファイルから読む込むようにしていたが、XMLデータの作り方で、DataSet のTableの生成方法が違っていたので幾つかのパターンを作成して調べてみました。

# 共通
ASP.NETのドロップボックスのリスト用クラス定義

```csharp
public class ListViewModel
{
    public string Value;
    public string DisplayText;
}
```

Valueには値、DisplayTextは「値:名前」としている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/e1968e27-d521-4ee4-3840-740918824281.png)

# パターン1
```xml:Sample.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Goods>
    <Good id = "1" lang="ja" name = "みかん" />
    <Good id = "2" lang="ja" name = "バナナ" />
    <Good id = "3" lang="ja" name = "いちご" />
    <Good id = "4" lang="ja" name = "りんご" />
    <Good id = "5" lang="ja" name = "ブドウ" />
</Goods>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/845376cc-9e68-efc6-41e1-2723208c93f0.png)

## ソースコード
```csharp
DataSet dsGood = new DataSet();
dsGood.ReadXml("Sample.xml");

var items = dsGood.Tables["Good"].AsEnumerable().
            Select(x => new ListViewModel()
            {
                Value = x.Field<string>("id"),
                DisplayText = x.Field<string>("id") + ":" + x.Field<string>("name")
            });

var items2 = items;
```

# パターン2
```xml:Sample2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Goods>
    <Good>
        <id>1</id>
        <lang>ja</lang>
        <name>りんご</name>
    </Good>
    <Good>
        <id>2</id>
        <lang>ja</lang>
        <name>バナナ</name>
    </Good>
    <Good>
        <id>3</id>
        <lang>ja</lang>
        <name>いちご</name>
    </Good>
    <Good>
        <id>4</id>
        <lang>ja</lang>
        <name>りんご</name>
    </Good>
    <Good>
        <id>5</id>
        <lang>ja</lang>
        <name>ブドウ</name>
    </Good>
</Goods>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/56997fc1-bc81-bf4d-6f12-9178f66c6150.png)

## ソースコード
パターン1とファイル名が違うのみ

```csharp
DataSet dsGood = new DataSet();
dsGood.ReadXml("Sample2.xml");

var items = dsGood.Tables["Good"].AsEnumerable().
            Select(x => new ListViewModel()
            {
                Value = x.Field<string>("id"),
                DisplayText = x.Field<string>("id") + ":" + x.Field<string>("name")
            });

var items2 = items;
```

# パターン3
```xml:Sample3.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Goods>
    <Good id="1">
        <lang>ja</lang>
        <name>りんご</name>
    </Good>
    <Good id="2">
        <lang>ja</lang>
        <name>バナナ</name>
    </Good>
    <Good id="3">
        <lang>ja</lang>
        <name>いちご</name>
    </Good>
    <Good id="4">
        <lang>ja</lang>
        <name>りんご</name>
    </Good>
    <Good id="5">
        <lang>ja</lang>
        <name>ブドウ</name>
    </Good>
</Goods>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/6d2a6e06-b456-001c-b676-3f4489936800.png)

## ソースコード
パターン1とファイル名が違うのみ

```csharp
DataSet dsGood = new DataSet();
dsGood.ReadXml("Sample3.xml");

var items = dsGood.Tables["Good"].AsEnumerable().
            Select(x => new ListViewModel()
            {
                Value = x.Field<string>("id"),
                DisplayText = x.Field<string>("id") + ":" + x.Field<string>("name")
            });

var items2 = items;
```

# パターン4
```xml:Sample4.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Goods>
    <Good id="1">
        <name lang="ja">みかん</name>
    </Good>
    <Good id="2">
        <name lang="ja">バナナ</name>
    </Good>
    <Good id="3">
        <name lang="ja">いちご"</name>
    </Good>
    <Good id="4">
        <name lang="ja">りんご"</name>
    </Good>
    <Good id="5">
        <name lang="ja">ブドウ</name>
    </Good>
</Goods>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/715939c2-a1fc-09cc-bb70-a9b694af5943.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/13597a78-aa77-31d1-f37b-9cd66b652fef.png)

## ソースコード
GoodテーブルとnameテーブルをIdで結合する必要があります。
nameテーブルには、親のGoodテーブルのIdが`Good_Id`列として自動で追加されます。
nameタグの名前は、`_Text`が列名に自動追加されて、`name_Text`列になります。

```csharp
DataSet dsGood = new DataSet();
dsGood.ReadXml("Sample4.xml");

var items = from g in dsGood.Tables["Good"].AsEnumerable()
            join n in dsGood.Tables["name"].AsEnumerable() on g.Field<int>("Good_Id") equals n.Field<int>("Good_Id")
            select new ListViewModel()
            {
                Value = g.Field<string>("id"),
                DisplayText = g.Field<string>("id") + ":" + n.Field<string>("Name_Text")
            };

var items2 = items;
```

# 最後に
XMLデータを上手く解析されて DataSet にセットされるようになっています。
パターン4になった時に、親子テーブルに分かれるのでLINQで内部結合を使う必要があります。
タグに属性がついている場合など、列名に`_Text`が自動追加されたりします。