Serde-rsの調査

## Rustでシリアライズ、デシリアライズしたい
ゲームのデータをセーブ、ロードするために、Rustのシリアライズ化フレームワーク[serde-rs](https://serde.rs/)を調査しました。
<img width="572" alt="スクリーンショット 2020-04-21 9.07.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319189/c1d9855b-d261-0910-b874-732e1109a8d0.png">


## [serde-rs](https://serde.rs/)
### 基本
データ構造を定義したら、以下のようにderiveを用いてSerializeトレートとDeserializeトレートをimplすると、データ構造をシリアライズ、デシリアライズできるようになります。シンプルな実装です。

```rust:kihon
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct PlayerType {
    MAN,
    COM
}

#[derive(Serialize, Desirialize)]
pub struct Player {
    id: u32,
    player_type: PlayerType
}

fn main() {
    let player = Player { id: 1, player_type: PlayerType::MAN };
    
    // json形式にシリアライズ
    // {"id":1,"player_type":"MAN"}
    let serialized = serde_json::to_string(&player).unwrap();

    // json形式からデシリアライズ
    // Player { id: 1, player_type: MAN }
    let deserialized: Sample = serde_json::from_str(&serialized).unwrap();

    // 確認
    match deserialized.aa {
        AA::BB => println!("aa"),
        _ => println!("nn"),
    }
}

```

### Serdeの特徴
1. Serdeデータモデルを用いて多くの型、フォーマットのデータを扱える
2. deriveを用いて、タグの名前やタグの有無を簡単に設定可能（属性設定）
3. データ欠損、非共通フィールドに動的に対応可能
4. カスタマイズ性が高い
 
###1. Serdeデータモデル
シリアライズする時、データ構造を中間的なSerdeデータモデルにマッピングし、そのモデルを意図したデータフォーマットに変換します。デシリアライズするときはその逆を行います。
このSerdeデータモデルの型はほとんどのRustの型と一致しているので、Rustのデータ構造を幅広くシリアライズできます。
OS依存の文字列型の問題を扱うときなど、ユースケースに応じたマッピングも可能であると、[こちら](https://serde.rs/#data-formats)に記述されています。

以下は利用可能なデータ構造とデータフォーマットです。

- [データフォーマット](https://serde.rs/#data-formats)
- [データ構造](https://serde.rs/#data-formats)


### 2. 属性の設定
serdeのderiveを用いて以下の３つのデータ属性を簡単に設定可能です。

- [Container属性](https://serde.rs/container-attrs.html#container-attributes)：structやenum全体の属性
- [Varient属性](https://serde.rs/variant-attrs.html#variant-attributes)：enumのvarientの１つの属性
- [Field属性](https://serde.rs/field-attrs.html#field-attributes)：structのフィールドの１つかenumのvarientの１つの属性

Container属性の例を１つ紹介します。

```rust:attributes
// シリアライズする時にタグの名前と内容物の名前を指定する
// データから指定のタグのものを抜き出したい時などに便利
#[derive(Serialize, Deserialize)]
#[serde(tag = "player_type", content = "detail")]  
pub enum PlayerType {
   MAN { id: u32 },
   COM { id: u32, com_level: ComLevel }
}

// JSONにシリアライズすると以下が出力される
// ・ 今回
// {"player_type": "MAN", "detail": {"id": ... }}, 
// {"player_type": "COM", "detail": {"id": ..., "com_level": ...}}

// ・ 何も指定しなかった場合
// {"MAN": {"id":...,}}
// {"COM": {"id":..., "com_level": ...}}
```

タグを消すときは#[serde(untagged)]を指定します。上記例だと、"MAN"、"COM"も出力されなくなります。

### 3. データ欠損、非共通フィールド
2項で説明したField属性についてserdeのderiveを用いるとデフォルト値を設定することができ、データ欠損に対応できます。 

```rust:default
#[derive(Serialize, Deserialize)]
pub struct Player {
   id: u32,
   #[serde(default)] // デシリアライズ時にこのフィールドの値が存在しない時にPlayerInfoのデファルト値を補う
   player_info: PlayerInfo
}

#[derive(Serialize, Deserialize)]
pub struct PlayerInfo {
   player_type: PlayerType,
   player_color: PlayerColor,
}
/// PlayerInfoのデフォルト関数
impl Default for PlayerInfo {
   fn default() -> Self {
        PlayerInfo {
           player_type: Player_Type::MAN,
           player_color: Color::Red,
        }
    }
}

/* 
例えばlet json = r#"[{"id": 2}]"#;
をデシリアライズするとデータ構造にplayer_infoの項をPlayerInfoのデフォルト値が補う。
*/ 
```

同様にField属性を用いて非共通フィールドにも対応可能です。同じデータ構造の中で、あるデータにないけど、別のデータには必要みたいな状況の時に使います。例えばPlayerタイプがマニュアルならコンピュータの強さは必要ないけど、CPUなら必要みたいな状況です。

```rust:flatten
#[derive(Serialize, Deserialize)]
struct Player {
    id: u32,
    player_type: PlayerType,

    #[serde(flatten)]
    extra: ComLevel,
}

// こうすることで、デシリアライズする時、"com_level": "Hard"がという項があっても大丈夫です。
```

### 4. カスタマイズ
かゆい所に手を届かせたいときはSerializeトレートとDeserializeトレートをderiveせずに自分でimplすることでカスタマイズできるそうです。
詳細は[こちら](https://serde.rs/custom-serialization.html#custom-serialization)。

## まとめ
かなり簡単に使えそうなので開発中のゲームに使っていこうと思いました。