jsonをファイルから読み込みたい (Rust)

はじめに

小小ネタです。

Rustを学びつつ次の技術書典のネタを作るためにjsonの読み込みが必要でした。 なのでRustでjsonの読み込み方法を調べました。

RustのGetting Startedをやったくらいの初学者向けを想定して書きます。

serde

jsonの読み込みにはserdeというフレームワークが使えるようです。以下のようにRustのデータ構造を効率的に汎用的にシリアライズ/デシリアライズするフレームワークだそうです。jsonを扱う時はserde_jsonを一緒に使います。

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically.

とりあえずREADMEの内容を実行してみました。ソースコードは以下のとおりです*1

Cargo.toml

[package]
name = "json"
version = "0.1.0"
authors = ["gkuga"]
edition = "2018"

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

[dependencies]
# The core APIs, including the Serialize and Deserialize traits. Always
# required when using Serde. The "derive" feature is only required when
# using #[derive(Serialize, Deserialize)] to make Serde work with structs
# and enums defined in your crate.
serde = { version = "1.0", features = ["derive"] }

# Each data format lives in its own crate; the sample code below uses JSON
# but you may be using a different one.
serde_json = "1.0"

src/main.rs

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&point).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

実行すると以下のようになります。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/json`
serialized = {"x":1,"y":2}
deserialized = Point { x: 1, y: 2 }

serdeのドキュメントを読むと #[derive(Serialize, Deserialize)] という記述によりコンパイラがPoint型*2SerializeDeserializeというトレイトを実装します。トレイトは他の言語のインターフェースのようなものです。それによりシリアライズ、デシリアライズされるようです。

ファイルの読み込み

ファイルの読み込みはserde_json::from_str()からserde_json::from_reader()に変えれば良いようです。

src/main.rs

use serde::{Deserialize, Serialize};

use std::fs::File;
use std::io::BufReader;

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let file = File::open("point.json").unwrap();
    let reader = BufReader::new(file);

    // Convert the JSON string from a file to a Point.
    let deserialized: Point = serde_json::from_reader(reader).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

point.jsonというファイルに{"x":1,"y":2}を書き込んで実行すると以下のようになりました。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/json`
deserialized = Point { x: 1, y: 2 }

おわり

今回自分がやりたいことをやるのにはこれくらいで十分なのですが、詳細を知るにはserde.rsにまとまっているようです。あとでざっと目を通してみます。

*1:コードの実行に必要なことはGetting Startedを参照ください

*2:Point構造体と言った方がいいのでしょうか?