んなまにのメモ帳

気が向いたときに更新されます。

Rustでserdeを使ってJSONのシリアライズとデシリアライズを試した。

はじめに

Rustでは、serdeというクレートを使ってデータのシリアライズ、デシリアライズができるらしいのでやり方を調べてみました。

serdeで対応できるデータフォーマットは、複数ありそれぞれがモジュール化されています。 JSONを使いたい場合は、serde_jsonを追加で読み込んで利用するようです。

準備

適当なプロジェクトを作成して、serdeserde_jsonをプロジェクトに追加します。

$ cargo new serde-json-sample
$ cd serde-json-sample
$ cargo add serde --features=derive
$ cargo add serde_json

Cargo.tomlのdependenciesは以下のような感じになりました。

[dependencies]
serde = { version = "1.0.189", features = ["derive"] }
serde_json = "1.0.107"

使ってみる

以下のようなJSON文字列をRustから読み込むことを考えます。

[
    {
        "Name": "Alice",
        "Age": 19
    },
    {
        "Name": "Bob",
        "Age": 18
    }
]

試してみる その1(とりあえずパースしてみる)

以下のような、main.rsでひとまず、

  1. JSONを構造体に変換
  2. 構造体をJSONに変換

が動くことが確認できました。

use serde::{Deserialize, Serialize};

#[allow(non_snake_case)]
#[derive(Deserialize, Serialize)]
struct Member {
    Name: String,
    Age: u8,
}

fn main() {
    let json_string = r#"
    [
        {
            "Name": "Alice",
            "Age": 19
        },
        {
            "Name": "Bob",
            "Age": 18
        }
    ]
    "#;

    let members: Vec<Member> = serde_json::from_str(&json_string).unwrap();

    for member in members.iter() {
        println!("name: {}, age: {}", member.Name, member.Age);
    }

    println!("{}", serde_json::to_string(&members).unwrap());
}

ポイント

  • JSONの内容をマッピングできるように、#[derive(Deserialize, Serialize)]でMember構造体にシリアライザーとデシリアライザーを実装する
  • serde_json::from_strJSON文字列を構造体に変換できる
  • serde_json::to_stringで構造体をJSON文字列に変換できる

実行結果は以下。

$ cargo run -q    
name: Alice, age: 19
name: Bob, age: 18
[{"Name":"Alice","Age":19},{"Name":"Bob","Age":18}]

この方法では、構造体のメンバー名はがJSONのキー名に依存してしまい、#[allow(non_snake_case)]の指定が必要でした。

試してみる その2(Snake caseとPascal Caseの自動変換をする)

Member構造体に、#[serde(rename_all = "PascalCase")]を指定すると、JSONのキーをPascalCase、構造体のメンバー名は、snake_caseとして認識してくれました。

修正したコードの差分は以下です。

diff --git a/src/main.rs b/src/main.rs
index 7fd87f4..a862c93 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,10 @@
 use serde::{Deserialize, Serialize};
 
-#[allow(non_snake_case)]
 #[derive(Deserialize, Serialize)]
+#[serde(rename_all = "PascalCase")]
 struct Member {
-    Name: String,
-    Age: u8,
+    name: String,
+    age: u8,
 }
 
 fn main() {
@@ -24,7 +24,7 @@ fn main() {
     let members: Vec<Member> = serde_json::from_str(&json_string).unwrap();
 
     for member in members.iter() {
-        println!("name: {}, age: {}", member.Name, member.Age);
+        println!("name: {}, age: {}", member.name, member.age);
     }
 
     println!("{}", serde_json::to_string(&members).unwrap());

実行結果は変わらずです。

$ cargo run -q
name: Alice, age: 19
name: Bob, age: 18
[{"Name":"Alice","Age":19},{"Name":"Bob","Age":18}]

変換するときにキー名を変更する方法は、他にも色々と用意されているようです。

serde.rs

まとめ

  • RustでJSONを扱うときは、serdeとserde_jsonというクレートが使える
  • JSON文字列をRustの構造体にマッピングできる。
  • JSONと構造体間でキー名の変換などをやりたい場合は、#[serde(rename_all = )]などを使うとできる