やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rust自習(シリアライズ serde 2 JSON5)

 JSON5で相互変換する。

成果物

参考

プロジェクト作成

$ cargo new serde_json5_hello --bin

Cargo.toml

[dependencies]
serde = { version = "1.0.97", features = ["derive"] }
serde_json = "1.0.40"
json5 = "0.2.5"

 実行してダウンロード&コンパイル

$ cargo run

 JSON5だけで2分くらい。全部あわせて6分くらいかかる。

コード

use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}
fn main() {
    let point = Point { x: 1, y: 2 };

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

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

 実行結果は以下。成功。

$ cargo run
...
serialized = {"x":1,"y":2}
deserialized = Point { x: 1, y: 2 }

JSON5

 せっかくなので、JSON5の特徴をもったテキストファイルからデシリアライズしてみる。

serde_json5_hello/test.json5

{
  /*
   * 複数行のコメント
   */
  // comments
  unquoted: 'and you can quote me on that',
  singleQuotes: 'I can use "double quotes" here',
  lineBreaks: "Look, Mom! \
No \\n's!",
  hexadecimal: 0xdecaf,
  leadingDecimalPoint: .8675309, andTrailing: 8675309.,
  positiveSign: +1,
  trailingComma: 'in objects', andIn: ['arrays',],
  "backwardsCompatible": "with JSON",
}

serde_json5_hello/src/main.rs

use json5;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct J5 {
    unquoted: String,
    singleQuotes: String,
    lineBreaks: String,
    hexadecimal: i32,
    leadingDecimalPoint: f64,
    andTrailing: f64,
    positiveSign: i32,
    trailingComma: String,
    andIn: Vec<String>,
    backwardsCompatible: String,
}
fn main() {
    if let Ok(content) = std::fs::read_to_string("./test.json5") {
        let deserialized: J5 = json5::from_str(&content).unwrap();
        println!("deserialized = {:?}", deserialized);
    }
}
$ cargo run
...
deserialized = J5 { unquoted: "and you can quote me on that", singleQuotes: "I can use \"double quotes\" here", lineBreaks: "Look, Mom! No \\n\'s!", hexadecimal: 912559, leadingDecimalPoint: 0.8675309, andTrailing: 8675309.0, positiveSign: 1, trailingComma: "in objects", andIn: ["arrays"], backwardsCompatible: "with JSON" }

 残念ポイントがいくつかある。

  • 整形されない
  • \が消える

 JSON5で見やすいように\や改行などを入れても、一度デシリアライズしてシリアライズしたテキストを上書きすると消える。まあ普通そうだろうが。

 配列がVec<T>で取得できるのは良い。(配列のコード例

Infinity, NaNを使う

失敗

 Infinity, NaNを使うとパニックした。

test.json5

{
  ...
  inf: +Infinity,
  ninf: -Infinity,
  nan: NaN,
}

main.rs

struct J5 {
    ...
    inf: i32,
    ninf: i32,
    nan: i32,
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Message("error parsing number")', src/libcore/result.rs:999:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

 error parsing numberということみたい。JSON5ファイルのInfinityを文字列として解釈したが構造体のほうがi32型なのでパニックした? そもそもInfinityNaNに対応しているのか? Rustで表現できるのか?

fn parse_number(pair: &Pair<'_, Rule>) -> Result<f64> {
    match pair.as_str() {
        "Infinity" => Ok(f64::INFINITY),
        "-Infinity" => Ok(f64::NEG_INFINITY),
        "NaN" | "-NaN" => Ok(f64::NAN),
...

 あるらしい。え、正数に+を付与できるのにInfinityには付与できないの?

成功

 +Infinityから+をとる。

test.json5

  inf: Infinity,
  ninf: -Infinity,
  nan: NaN,

 i32型をf64型にする。

main.rs

struct J5 {
    ...
    inf: f64,
    ninf: f64,
    nan: f64,
}

 実行。できた。

$ cargo run
...
deserialized = J5 { unquoted: "and you can quote me on that", singleQuotes: "I can use \"double quotes\" here", lineBreaks: "Look, Mom! No \\n\'s!", hexadecimal: 912559, leadingDecimalPoint: 0.8675309, andTrailing: 8675309.0, positiveSign: 1, trailingComma: "in objects", andIn: ["arrays"], backwardsCompatible: "with JSON", inf: inf, ninf: -inf, nan: NaN }

参考

所感

 構造体も動的に定義してくれると嬉しいのだが。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで