#serde-json #json-parser #deserialize-json #json #borrowed #deserialize #json-object

serde_json_borrow

提供将 JSON 反序列化到借用 DOM 的功能

17 个不稳定版本 (4 个重大变更)

0.5.1 2024年6月30日
0.5.0 2024年5月24日
0.4.7 2024年5月23日
0.3.0 2023年9月19日
0.1.4 2023年1月14日

#236解析器实现

Download history 25/week @ 2024-04-23 2/week @ 2024-05-07 654/week @ 2024-05-14 2031/week @ 2024-05-21 793/week @ 2024-05-28 488/week @ 2024-06-04 613/week @ 2024-06-11 601/week @ 2024-06-18 1068/week @ 2024-06-25 840/week @ 2024-07-02 769/week @ 2024-07-09 564/week @ 2024-07-16 647/week @ 2024-07-23 988/week @ 2024-07-30 1058/week @ 2024-08-06

3,414 每月下载量
2 个 crate 中使用 (通过 reductionml-core)

MIT 许可证

52KB
929 代码行

Crates.io Docs

Serde JSON Borrow

对于 ndjson 类型用例,实现高达 2 倍的 JSON 解析速度。

serde_json_borrow 通过尝试引用原始字节而不是将它们复制到 Strings 中,从 &'ctx str 反序列化 JSON 到 serde_json_borrow::Value<'ctx> DOM。

相比之下,默认的 serde_json 解析到拥有的 serde_json::Value。遇到的每个 String 都会被复制并因此分配。这对于人体工程学来说很好,但并不利于性能。特别是在 DOM 表示形式只是一个中间结构的情况下。

为了获得更高的性能,serde_json_borrow 将 JSON 对象的 (键,值) 推送到一个 Vec 中,而不是使用一个 BTreeMap。访问通过迭代器进行,这与迭代 BTreeMap 时的 API 相同。

拥有值

您可以使用 OwnedValue 将包含未解析 JSONString 解析到 Value 中,而无需担心生命周期,因为 OwnedValue 将获得 String 的所有权并引用它的切片,而不是创建副本。

限制

功能标志 cowkeys 使用 Cow<str> 而不是 &str 作为对象的键。这使支持键中的转义数据。如果没有 cowkeys 功能标志,则使用 &str,这不允许键中有任何 JSON 转义字符。

没有 cowkeys 功能标志的键中的 不受支持 字符列表(https://www.json.org/json-en.html)。

\" represents the quotation mark character (U+0022).
\\ represents the reverse solidus character (U+005C).
\/ represents the solidus character (U+002F).
\b represents the backspace character (U+0008).
\f represents the form feed character (U+000C).
\n represents the line feed character (U+000A).
\r represents the carriage return character (U+000D).
\t represents the character tabulation character (U+0009).

基准测试

cargobench

  • simple_json -> 带有某些键的扁平对象
  • hdfs -> 日志
  • wiki -> 少量键和大型文本正文
  • gh-archive -> 高度嵌套的对象
simple_json
serde_json               Avg: 139.29 MiB/s    Median: 139.53 MiB/s    [134.51 MiB/s .. 140.45 MiB/s]    
serde_json_borrow        Avg: 210.33 MiB/s    Median: 209.66 MiB/s    [204.08 MiB/s .. 214.28 MiB/s]    
SIMD_json_borrow         Avg: 140.36 MiB/s    Median: 140.44 MiB/s    [138.96 MiB/s .. 141.75 MiB/s]    
hdfs
serde_json               Avg: 284.64 MiB/s    Median: 284.60 MiB/s    [280.98 MiB/s .. 286.46 MiB/s]    
serde_json_borrow        Avg: 372.99 MiB/s    Median: 371.75 MiB/s    [365.97 MiB/s .. 379.96 MiB/s]    
SIMD_json_borrow         Avg: 294.41 MiB/s    Median: 294.96 MiB/s    [287.76 MiB/s .. 296.96 MiB/s]    
hdfs_with_array
serde_json               Avg: 194.50 MiB/s    Median: 200.41 MiB/s    [155.44 MiB/s .. 211.49 MiB/s]    
serde_json_borrow        Avg: 275.01 MiB/s    Median: 282.74 MiB/s    [208.35 MiB/s .. 289.78 MiB/s]    
SIMD_json_borrow         Avg: 206.34 MiB/s    Median: 210.52 MiB/s    [180.99 MiB/s .. 220.30 MiB/s]    
wiki
serde_json               Avg: 439.95 MiB/s    Median: 441.28 MiB/s    [429.97 MiB/s .. 444.82 MiB/s]    
serde_json_borrow        Avg: 484.74 MiB/s    Median: 485.29 MiB/s    [471.38 MiB/s .. 489.16 MiB/s]    
SIMD_json_borrow         Avg: 576.57 MiB/s    Median: 578.11 MiB/s    [554.03 MiB/s .. 586.18 MiB/s]    
gh-archive
serde_json               Avg: 176.21 MiB/s    Median: 176.37 MiB/s    [172.52 MiB/s .. 177.78 MiB/s]    
serde_json_borrow        Avg: 363.58 MiB/s    Median: 364.02 MiB/s    [355.28 MiB/s .. 374.10 MiB/s]    
SIMD_json_borrow         Avg: 383.66 MiB/s    Median: 386.94 MiB/s    [363.80 MiB/s .. 400.25 MiB/s]    

待办事项

而不是将 JSON 对象解析到 Vec 中,可以通过功能标志启用 BTreeMap

可变性

OwnedValue 是不可变的,这是其设计。如果您需要修改 Value,则可以将其转换为 serde_json::Value

示例

以下是一个为什么可变性不会工作的示例

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bb0b919acc8930e71bdefdfc6a6d5240

use std::io;

use std::borrow::Cow;


/// Parses a `String` into `Value`, by taking ownership of `String` and reference slices from it in
/// contrast to copying the contents.
///
/// This is done to mitigate lifetime issues.
pub struct OwnedValue {
    /// Keep owned data, to be able to safely reference it from Value<'static>
    _data: String,
    value: Vec<Cow<'static, str>>,
}

impl OwnedValue {
    /// Takes ownership of a `String` and parses it into a DOM.
    pub fn parse_from(data: String) -> io::Result<Self> {
        let value = vec![Cow::from(data.as_str())];
        let value = unsafe { extend_lifetime(value) };
        Ok(Self { _data: data, value })
    }

    /// Returns the `Value` reference.
    pub fn get_value<'a>(&'a self) -> &'a Vec<Cow<'a, str>> {
        &self.value
    }
    /// This cast will break the borrow checker
    pub fn get_value_mut<'a>(&'a mut self) -> &'a mut Vec<Cow<'a, str>> {
        unsafe{std::mem::transmute::<&mut Vec<Cow<'static, str>>, &mut Vec<Cow<'a, str>>>(&mut self.value)}
    }
}

unsafe fn extend_lifetime<'b>(r: Vec<Cow<'b, str>>) -> Vec<Cow<'static, str>> {
    std::mem::transmute::<Vec<Cow<'b, str>>, Vec<Cow<'static, str>>>(r)
}

fn main() {
    let mut v1 = OwnedValue::parse_from(String::from("oop")).unwrap();
    let mut v2 = OwnedValue::parse_from(String::from("oop")).unwrap();
    let oop = v1.get_value().last().unwrap().clone();
    v2.get_value_mut().push(oop);
    drop(v1);
    let oop = v2.get_value_mut().pop().unwrap();
    println!("oop: '{oop}'");
}

依赖项

~0.5–1MB
~20K SLoC