RustでデータをJSONやYAML等にserialize/deserializeする方法

この記事は[Wanoアドベントカレンダー](https://qiita.com/advent-calendar/2018/wano-group)の4日目の記事です。

## TL;DR

* 使うcrate
   * [serde](https://crates.io/crates/serde)
   * [serde_derive](https://crates.io/crates/serde_derive)
   * serdeに対応した各フォーマットを扱うcrate ([serde_json](https://crates.io/crates/serde_json)等)
* serdeはserialize/deserializeの為の仕組みを提供する
* serde_json等の各フォーマット毎のserdeに対応したcrateを使って具体的なフォーマットとの変換を行う

## Rustでserialize/deserializeを行うには

[serde](https://crates.io/crates/serde)と[serde_derive](https://crates.io/crates/serde_derive)というcrateを使います。
この2つのcrateを使って`Serialize`/`Deserialize` traitを構造体などに実装する事でserializeが出来るようになります。
serde_deriveが自動実装のマクロを提供しているので、ユーザーは構造体等の定義の上に`#[derive(Serialize, Deserialize)]`と書くだけでOKです。

## JSONとのserialize/deserializeを実装してみる

### serialize

まずはJSONへのserializeを実装してみましょう。
`$ cargo new serde_example`という感じでプロジェクトを作ります。
できましたら今作ったプロジェクトのディレクトリに移動して(`$ cd serde_example`)、`Cargo.toml`を開いて`[dependencies]`の下に次の3行を追加します。

~~~~toml
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
~~~~

次に`src/main.rs`を開いて次の様に編集します。

~~~~rust
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[derive(Debug, Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
}

fn main() -> Result<(), Box<std::error::Error>> {
    let tarou = Person { name: "太郎".to_string(), age: 18 };
    let json = serde_json::to_string(&tarou)?;
    println!("{}", json);

    Ok(())
}
~~~~

`$ cargo run`で実行します。

必要なcrateをダウンロードしたりコンパイルしたり等のメッセージが色々出てると思いますが、最後に次のテキストが表示されていれば成功です。

~~~~text
{"name":"太郎","age":18}
~~~~

これでJSONへのシリアライズが出来ました。

ユーザーは構造体等の上に`#[derive(Serialize, Deserialize)]`と書き（必要に応じて他のtrait等も一緒に書く）、`Serialize`と`Deserialize`を実装します。
実装したらserdeに対応している目的のフォーマット用のcrateで実際に変換します。
ここではserde_jsonがこれに当たり、`serde_json::to_string(&tarou)`とすることで構造体からJSONへと変換しています。

### deserialize

次にJSONからのdeserializeを実装してみましょう。
上で編集した`main.rs`の`main`関数を次の様に変更します。

~~~~rust
fn main() -> Result<(), Box<std::error::Error>> {
    let tarou = Person { name: "太郎".to_string(), age: 18 };
    let json = serde_json::to_string(&tarou)?;
    println!("{}", json);

    // ここが追加分
    let json = r#"{ "name": "花子", "age": 68 }"#;
    let hanako: Person = serde_json::from_str(json)?;
    println!("{:?}", hanako);

    Ok(())
}
~~~~

`$ cargo run`で実行します。

~~~~text
{"name":"太郎","age":18}
Person { name: "花子", age: 68 }
~~~~

Person {～}の行が増えました。JSON文字列から構造体に正しく変換できています。

## serdeに対応したcrate

serdeに対応しているcrateには次の様な物があります。

| Format                | crate                          | github                                  |
| --------------        | ------------------------------ | --------------------------------------- |
| JSON                  | serde_json                     | https://github.com/serde-rs/json        |
| YAML                  | serde_yaml                     | https://github.com/dtolnay/serde-yaml   | 
| MessagePack           | rmp, rmp-serialize, rmps, rmpv | https://github.com/3Hren/msgpack-rust   |
| TOML                  | toml                           | https://github.com/alexcrichton/toml-rs |
| x-www-form-urlencoded | serde_urlencoded               | https://github.com/nox/serde_urlencoded |

[serdeのドキュメント](https://serde.rs/#data-formats)にその他の形式のcrateの紹介もあります。