#json #serde-json #serialization #json-file #serde

no-std typed-json

一种JSON序列化文件格式

5个版本

0.1.1 2023年11月10日
0.1.0 2023年11月7日

#328 in 编码

每月 29 次下载

MIT/Apache

44KB
855

Typed JSON — 最新版本

Typed JSON提供了一个json!宏,用于构建具有非常自然JSON语法的impl serde::Serialize类型。

use typed_json::json;

// The type of `john` is `impl serde::Serialize`
let john = json!({
    "name": "John Doe",
    "age": 43,
    "phones": [
        "+44 1234567",
        "+44 2345678"
    ]
});

// Convert to a string of JSON and print it out
println!("{}", serde_json::to_string(&john).unwrap());

关于json!宏的一个巧妙之处是,您可以直接将变量和表达式插入到正在构建的JSON值中。Serde将在编译时检查您插入的值是否能够表示为JSON。

let full_name = "John Doe";
let age_last_year = 42;

fn random_phone() -> String {
    "0".to_owned()
}

// The type of `john` is `impl serde::Serialize`
let john = typed_json::json!({
    "name": full_name,
    "age": age_last_year + 1,
    "phones": [
        format!("+44 {}", random_phone())
    ]
});

serde_json的比较

此crate提供了一个serde_json::json!()的泛型版本。这意味着它执行0次分配,并为您表示的JSON对象创建一个自定义类型。对于一次性JSON文档,这最终使得编码速度快得多。这与serde_json::json!()语法100%兼容,自serde_json = "1.0.108"以来。

基准测试

以下基准测试表明将一个复杂的深层嵌套JSON文档序列化为String

注意:typed_json_core基准测试使用serde-json-core将数据编码为heapless::String

Timer precision: 41 ns
serialize_string    fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ serde_json       765.3 ns      │ 15.1 µs       │ 807 ns        │ 824.9 ns      │ 100000  │ 800000
├─ typed_json       148.1 ns      │ 1.606 µs      │ 153.3 ns      │ 156 ns        │ 100000  │ 3200000
╰─ typed_json_core  217.1 ns      │ 2.991 µs      │ 228.8 ns      │ 240.4 ns      │ 100000  │ 3200000

注意:基准测试使用serde_json::to_string,因为它比ToString/Display实现快得多,适用于serde_json::jsontyped_json::json

无std支持

您可以使用仅含coretyped_json。禁用默认的"std"功能。

[dependencies]
typed_json = { version = "0.1", default-features = false }

Serialize 类型编码为 JSON

您可能需要 serde_json 并启用 alloc 功能

[dependencies]
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }

或者 serde-json-core,无需依赖 alloc

[dependencies]
serde-json-core = "0.5.1"

工作原理

注意:这些都是实现细节,都不是稳定的 API

let data = json!({
    "codes": [400, value1, value2],
    "message": value3,
    "contact": "contact support at [email protected]"
});

扩展成类似

let data = typed_json::__private::Map(hlist![
    typed_json::__private::KV::Pair(
        typed_json::__private::Expr("codes"),
        typed_json::__private::Array(hlist![
            typed_json::__private::Expr(400),
            typed_json::__private::Expr(value1),
            typed_json::__private::Expr(value2),
        ]),
    ),
    typed_json::__private::KV::Pair(
        typed_json::__private::Expr("message"),
        typed_json::__private::Expr(value3)
    ),
    typed_json::__private::KV::Pair(
        typed_json::__private::Expr("contact"),
        typed_json::__private::Expr("contact support at [email protected]")
    ),
]);

的形式,其中 hlist![a, b, c, d, e] 将扩展成

(a, ((b, c), (d, e)))

编译时基准测试

没有真正的零成本抽象。然而,有时似乎 typed-json 编译速度比 serde_json 快,有时则相反。

我使用了来自 https://kubernetesjsonschema.dev/ 的大型服务 JSON 进行编译时间测量。

基准测试细节

许多小文档

在这个测试中,我已经将上述 JSON 文件分割成 31 个合理大小的文档

调试

$ hyperfine \
    --command-name "typed_json" \
    "pushd tests/crates/stress3 && touch src/main.rs && cargo build" \
    --command-name "serde_json" \
    "pushd tests/crates/stress4 && touch src/main.rs && cargo build"

Benchmark 1: typed_json
  Time (mean ± σ):     148.6 ms ±   3.7 ms    [User: 141.2 ms, System: 82.0 ms]
  Range (min … max):   143.3 ms … 157.0 ms    20 runs
 
Benchmark 2: serde_json
  Time (mean ± σ):     151.7 ms ±   4.8 ms    [User: 134.9 ms, System: 98.5 ms]
  Range (min … max):   143.2 ms … 163.0 ms    20 runs
 
Summary
  typed_json ran
    1.02 ± 0.04 times faster than serde_json

发布

$ hyperfine \
    --command-name "typed_json" \
    "pushd tests/crates/stress3 && touch src/main.rs && cargo build --release" \
    --command-name "serde_json" \
    "pushd tests/crates/stress4 && touch src/main.rs && cargo build --release"

Benchmark 1: typed_json
  Time (mean ± σ):     538.3 ms ±   7.1 ms    [User: 877.5 ms, System: 65.7 ms]
  Range (min … max):   527.4 ms … 550.9 ms    10 runs
 
Benchmark 2: serde_json
  Time (mean ± σ):      1.003 s ±  0.013 s    [User: 1.194 s, System: 0.075 s]
  Range (min … max):    0.972 s …  1.020 s    10 runs
 
Summary
  typed_json ran
    1.86 ± 0.04 times faster than serde_json

一次性大文档

在这个测试中,我已经将单个 JSON 文件原样包含在内。我认为这不是一个现实的应用场景,但仍很有趣

调试

$ hyperfine \
    --command-name "typed_json" \
    "pushd tests/crates/stress1 && touch src/main.rs && cargo build" \
    --command-name "serde_json" \
    "pushd tests/crates/stress2 && touch src/main.rs && cargo build"

Benchmark 1: typed_json
  Time (mean ± σ):     157.5 ms ±   6.1 ms    [User: 147.9 ms, System: 83.5 ms]
  Range (min … max):   152.1 ms … 178.4 ms    18 runs
 
Benchmark 2: serde_json
  Time (mean ± σ):     151.7 ms ±   4.5 ms    [User: 133.6 ms, System: 97.9 ms]
  Range (min … max):   145.1 ms … 162.4 ms    18 runs
 
Summary
  serde_json ran
    1.04 ± 0.05 times faster than typed_json

发布

$ hyperfine \
    --command-name "typed_json" \
    "pushd tests/crates/stress1 && touch src/main.rs && cargo build --release" \
    --command-name "serde_json" \
    "pushd tests/crates/stress2 && touch src/main.rs && cargo build --release"

Benchmark 1: typed_json
  Time (mean ± σ):      1.501 s ±  0.012 s    [User: 2.324 s, System: 0.090 s]
  Range (min … max):    1.480 s …  1.520 s    10 runs
 
Benchmark 2: serde_json
  Time (mean ± σ):     947.3 ms ±  20.4 ms    [User: 1142.0 ms, System: 71.2 ms]
  Range (min … max):   918.7 ms … 989.0 ms    10 runs
 
Summary
  serde_json ran
    1.58 ± 0.04 times faster than typed_json

结论

我认为我无法断言 typed-json 在标准使用中引入了编译时退步。在极端情况下,它可能需要编译更多类型,但在标准使用中,它可以重用大量的先前编译

依赖关系

~110–440KB