RustでJSONの読み書きを行う

607, 2022-12-21

目次

RustでJSONの読み書き

RustでJSONの読み書きを行う方法をまとめました。
RustではJSONのシリアライズ・デシリアライズは「serde」という外部ライブラリを使うのが一般的になっています。
このライブラリの使い方を具体的に解説していきます。

外部ライブラリのインストール

外部ライブラリは「serde」と「serde_json」をインストールします。
JSONファイルのパースの場合はこの2つが必要ですので注意してください。
で、この2つのライブラリなんですがserdeのインストール方法にコツがあってこれを記載しているドキュメントがほとんどありませんでした。
そのため最終的にGitHubのページで必要な情報を見つけました。
コツというのは↓のようにCargo.tomlを書くことです。

[package]
name = "rust_json"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.1"

↑の[dependecies]の項目の

serde = { version = "1.0", features = ["derive"] }

の部分がコツです。
features = ["derive"」」となっている部分に注意してください。
ここの記述が無いとserdeSerializeDeserializeが使えないです。
ですので必ずこのフォーマットで書くようにしてください。

筆者はこれに気づかず30分ほど時間を溶かしました。

(^ _ ^)

帰ってこい、30分

(・ v ・)

帰りまてん

ライブラリをuseする

必要なライブラリをソースコードの先頭でuseしておきます。

use serde_json;
use serde::{Serialize, Deserialize};
use std::io::Write;

serde_jsonはJSONへのシリアライズ/デシリアライズで、serde::{Serialize, Deserialize}は構造体のderiveで必要になります。
std::io::Writeはファイル入出力で必要になります。

構造体をシリアライズ/デシリアライズする

シリアライズというのは構造体を文字列に変換することです。
逆のデシリアライズは文字列を構造体にします。

serdeでは↓のようにするとシリアライズとデシリアライズができます。

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

fn test_serialize_and_deserialize() {
    let animal = Animal { name: String::from("Mike"), age: 20 };
    let serialized = serde_json::to_string(&animal).unwrap();
    println!("serialized: {}", serialized);

    let deserialized: Animal = serde_json::from_str(&serialized).unwrap();
    println!("deserialized: {:?}", deserialized);
}

出力結果↓。

serialized: {"name":"Mike","age":20}
deserialized: Animal { name: "Mike", age: 20 }

構造体をシリアライズ/デシリアライズするには構造体にderiveを付ける必要があります。

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

こうするとこの構造体をシリアライズ/デシリアライズできるようになります。

構造体をシリアライズするには

    let serialized = serde_json::to_string(&animal).unwrap();

のようにserde_json::to_string()を使います。
こうすると構造体を文字列に変換できます。

デシリアライズするには

    let deserialized: Animal = serde_json::from_str(&serialized).unwrap();

このようにserde_json::from_str()を使います。

入れ子になっている構造体をシリアライズ/デシリアライズする

構造体を入れ子にしたい場合は、つまり複数の構造体で構成したい場合はそれぞれの構造体にすべてderiveを付けます。

#[derive(Serialize, Deserialize, Debug)]
struct PTag {
    content: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct DivTag {
    p_tags: Vec<PTag>,
}

fn test_div_tag() {
    let mut div_tag = DivTag { p_tags: vec![] };
    div_tag.p_tags.push(PTag { content: String::from("hige") });
    div_tag.p_tags.push(PTag { content: String::from("hoge") });

    let serialized = serde_json::to_string(&div_tag).unwrap();
    println!("serialized: {}", serialized);

    let deserialized: DivTag = serde_json::from_str(&serialized).unwrap();
    println!("deserialized: {:?}", deserialized);    
}

出力結果↓。

serialized: {"p_tags":[{"content":"hige"},{"content":"hoge"}]}
deserialized: DivTag { p_tags: [PTag { content: "hige" }, PTag { content: "hoge" }] }

↑の場合、DivTagPTagのベクタを持っていますがこれもちゃんとシリアライズされます。
このように構造体を入れ子にするときはそれぞれの構造体にderiveを付けましょう。

JSONファイルへの書き込み/読み込み

シリアライズしたデータのファイルへの書き込み、またはファイルデータのデシリアライズは↓のように行います。

fn test_file() {
    let animal = Animal { name: String::from("Mike"), age: 20 };
    let mut fout = std::fs::OpenOptions::new()
        .write(true)
        .create(true)
        .open("data.json")
        .unwrap();
    let serialized = serde_json::to_string(&animal).unwrap();
    fout.write_all(serialized.as_bytes());

    let content = std::fs::read_to_string("data.json").unwrap();
    let deserialized: Animal = serde_json::from_str(&content).unwrap();
    println!("deserialized: {:?}", deserialized);
}

出力結果↓。

deserialized: Animal { name: "Mike", age: 20 }

シリアライズしたデータは文字列です。
ですので普通のファイル入出力でファイルにデータを書き込み可能です。

また文字列のデータが書き込まれたファイルを開いてデータを読み込み、それをデシリアライズすれば構造体に変換可能になります。
「構造体 -> 文字列(JSON)-> ファイル -> 文字列(JSON) -> 構造体」
という流れになります。

おわりに

今回はRustでJSONファイルを読み書きする方法を解説しました。
serdeを使うと簡単に読み書きできるので開発もはかどりますね。

(^ _ ^)

シリアライズだ!

(・ v ・)

デシリアライズだ!



この記事のアンケートを送信する