YAMLについてまとめてみた

# 概要
YAMLファイルについて勉強を兼ねてまとめてみました。

# 目次
- [歴史](#歴史)
- [記述構成](#記述構成)
- [デシリアライズ方法](#デシリアライズ方法)
- [シリアライズ方法](#シリアライズ方法)
- [JSONとの違い](#jsonとの違い)
- [その他](#その他)

# 歴史

YAMLは2001年にIngy döt Net、Clark Evans、Oren Ben-Kikiによって開発されました。最初は「Yet Another Markup Language (もうひとつ別のマークアップ言語) 」から名前をとっていましたが、後から「YAML Ain't Markup Language (YAMLはマークアップ言語ではない) 」という再帰的頭字語を使用したものに後付けで変更されています。

参考：[YAML ABOUT](https://yaml.org/about.html)

# 記述構成
yamlの中は以下のような記述になっています。
```yaml
# スカラー値（基本的なデータ型）
名前: 歩毛太郎
年齢: 30
性別: 男
国籍: 日本

# 配列（リスト）
趣味:
  - 読書
  - 音楽
  - スポーツ

# 連想配列（辞書）
連絡先:
  電話: 012-3456-7890
  メール: hoge@email.com
  住所:
    都道府県: 東京都
    市区町村: 渋谷区
    番地: 1-1-1

```
### 基本的な記述
- KeyとValueの形式で、とコロン（:）で分けます
- コロンの後は、半角スペースを入れた後に値を記述します

### コメントアウト
- コメントアウトは`#`で記載します

```yaml
# スカラー値（基本的なデータ型）
```

### スカラー
- 配列や辞書になっていない、基本的なデータの部分をスカラーと呼びます

```yaml
名前: 歩毛太郎
年齢: 30
性別: 男
国籍: 日本
```

### 配列
- `-`を使用して列挙することで、配列を表すことができます

```yaml
趣味:
  - 読書
  - 音楽
  - スポーツ
```

### 連想配列 (辞書)
- インデントを使って階層構造にすることで、連想配列にすることができます
- 連想配列もキーと値のペアを複数持つことができます

```yaml
連絡先:
  電話: 012-3456-7890
  メール: hoge@email.com
  住所:
    都道府県: 東京都
    市区町村: 渋谷区
    番地: 1-1-1
```

# デシリアライズ方法
C#を使用したデシリアライズ方法を記載しました。
Nugetから[YamlDotNet](https://www.nuget.org/packages/yamldotnet)のインストールをしておく必要があります。

### パターン1

[こちら](#yamlファイルの構成)のyamlをもとにデシリアライズを行います。

#### データクラス
```C#
        public class Person
        {
            public string 名前 { get; set; }
            public int 年齢 { get; set; }
            public string 性別 { get; set; }
            public string 国籍 { get; set; }
            public List<string> 趣味 { get; set; }
            public ContactInfo 連絡先 { get; set; }
        }

        public class ContactInfo
        {
            public string 電話 { get; set; }
            public string メール { get; set; }
            public Address 住所 { get; set; }
        }

        public class Address
        {
            public string 都道府県 { get; set; }
            public string 市区町村 { get; set; }  
            public string 番地 { get; set; }
        }
```

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

```C#
using YamlDotNet.Serialization;

    Person person;
    using (var reader = new StreamReader(@"yamlファイルまでのパス"))
    {
        person = deserializer.Deserialize<Person>(reader);
    }
```

### パターン2
```yaml
# 会社情報
company:
  name: 株式会社サンプル
  founded_date: 2020-01-15
  
  # 設定情報
  settings:
    timezone: Asia/Tokyo
    currency: JPY
    features:
      - advanced_reporting
      - automated_backup

  # 部署情報
  departments:
    department1:
      name: 開発部
      manager_name: 田中 一郎
      team_leads:
          - 田中 二郎
          - 田中 三郎
      employees:
        employee1:
            id: 00001
            name: 田中 四郎
            role: シニアエンジニア
            salary: 850000
            active: true
            contact:
              email: shiro-tanaka@sample.co.jp
              phone: 012-3456-7890
        employee2:
            id: 00002
            name: 田中 五郎
            role: ジュニアエンジニア
            salary: 600000
            active: true
            contact:
              email: goro-tanaka@sample.co.jp
              phone: 098-7654-3210
    department2:
      name: 営業部
      manager_name: 山田 花子
      team_leads:
        - 山田 月子
      employees:
        employee1:
            id: 00003
            name: 山田 正子
            role: アカウントマネージャー
            salary: 750000
            active: true
            contact:
              email: masako-yamada@sample.co.jp
              phone: 000-1111-2222
  # 製品情報
  products:
    product1:
      id: prod001
      name: ロボット君1号
      price: 5000000
      categories:
        - 作業用ロボット
        - 法人向け
      specs:
        system_requirements:
          os: hogeOS
          cpu: hogeCPU
          memory: 16GB以上
        features:
          - 高速な作業
          - 高度な知能
        support_options:
          premium_support:
            response_time: 1時間
            channels:
              - phone
              - email
          standard_support:
            response_time: 24時間
            channels:
              - phone
              - email
    product2:              
      id: prod002
      name: ロボット君2号
      price: 5000000
      categories:
        - 鑑賞用ロボット
        - 個人向け
      specs:
        system_requirements:
          os: hogeOS
          cpu: hogeCPU
          memory: 8GB以上
        features:
          - 愛らしい見た目
          - よく懐く
        support_options:
          premium_support:
            response_time: 2時間
            channels:
              - phone
              - email
          standard_support:
            response_time: 24時間
            channels:
              - phone
              - email
```
#### データクラス
```C#
        public class Root
        {
            [YamlMember(Alias = "company")]
            public Company Company { get; set; }
        }

        public class Company
        {
            [YamlMember(Alias = "name")]
            public string Name { get; set; }

            [YamlMember(Alias = "founded_date")]
            public DateTime FoundedDate { get; set; }

            [YamlMember(Alias = "settings")]
            public Settings Settings { get; set; }

            [YamlMember(Alias = "departments")]
            public Departments Departments { get; set; }


            [YamlMember(Alias = "products")]
            public Products Products { get; set; }
        }

        public class Settings
        {
            [YamlMember(Alias = "timezone")]
            public string Timezone { get; set; }
            [YamlMember(Alias = "currency")]
            public string Currency { get; set; }
            [YamlMember(Alias = "features")]
            public List<string> Features { get; set; }
        }

        public class Departments
        {
            [YamlMember(Alias = "department1")]
            public Department Department1 { get; set; }

            [YamlMember(Alias = "department2")]
            public Department Department2 { get; set; }
        }

            public class Department
        {
            [YamlMember(Alias = "name")]
            public string Name { get; set; }

            [YamlMember(Alias = "manager_name")]
            public string ManagerName { get; set; }

            [YamlMember(Alias = "team_leads")]
            public List<string> TeamLeads { get; set; }

            [YamlMember(Alias = "employees")]
            public Employees Employees { get; set; }
        }

        public class Employees
        {
            [YamlMember(Alias = "employee1")]
            public Employee Employee1 { get; set; }

            [YamlMember(Alias = "employee2")]
            public Employee Employee2 { get; set; }
        }

        public class Employee
        {
            [YamlMember(Alias = "id")]
            public string Id { get; set; }

            [YamlMember(Alias = "name")]
            public string Name { get; set; }

            [YamlMember(Alias = "role")]
            public string Role { get; set; }

            [YamlMember(Alias = "salary")]
            public decimal Salary { get; set; }

            [YamlMember(Alias = "active")]
            public bool Active { get; set; }

            [YamlMember(Alias = "contact")]
            public ContactInfo Contact { get; set; }
        }

        public class ContactInfo
        {
            [YamlMember(Alias = "email")]
            public string Email { get; set; }
            [YamlMember(Alias = "phone")]
            public string Phone { get; set; }
        }

        public class Products
        {
            [YamlMember(Alias = "product1")]
            public Product Product1 { get; set; }

            [YamlMember(Alias = "product2")]
            public Product Product2 { get; set; }
        }

        public class Product
        {
            [YamlMember(Alias = "id")]
            public string Id { get; set; }

            [YamlMember(Alias = "name")]
            public string Name { get; set; }

            [YamlMember(Alias = "price")]
            public decimal Price { get; set; }

            [YamlMember(Alias = "categories")]
            public List<string> Categories { get; set; }

            [YamlMember(Alias = "specs")]
            public Specs Specs { get; set; }
        }

        public class Specs
        {
            [YamlMember(Alias = "system_requirements")]
            public SystemRequirements SystemRequirements { get; set; }

            [YamlMember(Alias = "features")]
            public List<string> Features { get; set; }

            [YamlMember(Alias = "support_options")]
            public SupportOptions SupportOptions { get; set; }
        }

        public class SystemRequirements
        {
            [YamlMember(Alias = "os")]
            public string Os { get; set; }

            [YamlMember(Alias = "cpu")]
            public string Cpu { get; set; }

            [YamlMember(Alias = "memory")]
            public string Memory { get; set; }
        }

        public class SupportOptions
        {
            [YamlMember(Alias = "premium_support")]
            public PremiumSupport PremiumSupport { get; set; }
            [YamlMember(Alias = "standard_support")]
            public StandardSupport StandardSupport { get; set; }
        }

        public class PremiumSupport
        {
            [YamlMember(Alias = "response_time")]
            public string ResponseTime { get; set; }

            [YamlMember(Alias = "channels")]
            public List<string> Channels { get; set; }
        }

        public class StandardSupport
        {
            [YamlMember(Alias = "response_time")]
            public string ResponseTime { get; set; }

            [YamlMember(Alias = "channels")]
            public List<string> Channels { get; set; }
        }
```

:::note
プロパティにYamlMember属性を使用することで、プロパティ名とyaml内の名前が同じではなくても、紐づけることができます。
:::

#### デシリアライズ方法
```C#
using YamlDotNet.Serialization;

    var deserializer = new DeserializerBuilder().Build();
    Root root;
    using (var reader = new StreamReader(@"yamlファイルまでのパス"))
    {
        root = deserializer.Deserialize<Root>(reader);
    }
```

:::note
以下のように、連想配列なのに`-`を入力してしまっていたりすると、デシリアライズがうまくいきません。目視では意外と見つけにくいので要注意です。

```yaml
# 部署情報
departments:
  - name: 開発部
    manager_name: 田中 一郎
    team_leads:
      - 田中 二郎
      - 田中 三郎
```

上記の場合、正しくは
```yaml
  - name: 開発部
```
が
```yaml
    name: 開発部
```
になります。
:::


# シリアライズ方法
以下のようにしてシリアライズができます。

`person`は[パターン1](#パターン1)でデシリアライズした際のpersonクラスです。

```C#
using YamlDotNet.Serialization;

    var serializer = new SerializerBuilder()
        .WithNamingConvention(CamelCaseNamingConvention.Instance)
        .Build();
    string serializedData = serializer.Serialize(person);
```

# JSONとの違い
JSONは主にアプリ間によるデータの受け渡しの際によく使用されます。YAMLはデータの受け渡しよりも、どちらかといえば設定ファイルによく使用されるイメージです。YAMLはJSONよりもシンプルに記載できるため、人がよく触るような設定ファイルに向いています。

# その他
yamlの記述構成が正しいか判定してくれるサイトがありました。
[YAML Lint](https://www.yamllint.com/)

# 参考
- [動画【YAMLの書き方】PythonでYAMLファイルを操作 〜プログラミング入門〜 初心者向け](https://www.youtube.com/watch?v=rpbq6vXcNbM)