13个重大版本
0.14.0 | 2024年4月2日 |
---|---|
0.13.0 | 2024年3月4日 |
0.12.0 | 2024年3月2日 |
589 在 解析器实现
每月下载量203次
1.5MB
1.5K SLoC
JSN
一个具有低分配开销的可查询的流式JSON提取解析器。
- 提取解析器? 解析器实现为一个迭代器,它发出标记
- 流式? 正在解析的JSON文档永远不会完全加载到内存中。它是逐字节读取和验证的。这使得它非常适合处理大型JSON文档
- 可查询的? 您可以配置解析器,以便只发出和为输入的感兴趣部分分配标记。
JSON应遵循 RFC 8259。然而,也支持换行符分隔的JSON和连接的JSON格式。
输入可以来自任何实现了 Read
特性的来源(例如文件、字节切片、网络套接字等..)
基本用法
use jsn::{TokenReader, mask::*, Format};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let data = r#"
{
"name": "John Doe",
"age": 43,
"nicknames": [ "joe" ],
"phone": {
"carrier": "Verizon",
"numbers": [ "+44 1234567", "+44 2345678" ]
}
}
{
"name": "Jane Doe",
"age": 32,
"nicknames": [ "J" ],
"phone": {
"carrier": "AT&T",
"numbers": ["+33 38339"]
}
}
"#;
let mask = key("numbers").and(index(0))
.or(key("name"))
.or(key("age"));
let mut iter = TokenReader::new(data.as_bytes())
.with_mask(mask)
.with_format(Format::Concatenated)
.into_iter();
assert_eq!(iter.next().unwrap()?, "John Doe");
assert_eq!(iter.next().unwrap()?, 43);
assert_eq!(iter.next().unwrap()?, "+44 1234567");
assert_eq!(iter.next().unwrap()?, "Jane Doe");
assert_eq!(iter.next().unwrap()?, 32);
assert_eq!(iter.next().unwrap()?, "+33 38339");
assert_eq!(iter.next(), None);
Ok(())
}
快速说明
与传统流式解析器一样,解析器发出JSON标记。不同之处在于您可以用一种“有趣”的方式查询它们。最好的类比是位掩码。
如果您可以使用位运算符 AND
来提取一个位模式
input : 0101 0101
AND
bitmask : 0000 1111
=
pattern : 0000 0101
为什么不能使用位运算符 AND
来提取JSON标记模式?
input : { "hello": { "name" : "world" } }
AND
json mask : {something that extracts a "hello" key}
=
pattern : _ ________ { "name" : "world" } _
这个 {某种提取 ""hello"" 键的 }
是这个crate提供的。
内存占用
jsn
允许您选择感兴趣的JSON部分。您如何处理这些部分以及它们在内存中保留多长时间由您决定。
为了说明这一点,我将使用Valgrind DHAT工具来分析两个类似程序的堆内存使用情况。这两个程序都从JSON文件中读取和提取键。我将使用来自这里的sf-city-lots json文件(189 MB)。
examples/store-tokens.rs
:此程序将提取的标记保留在Vec中examples/print-tokens.rs
:此程序按遇到顺序打印标记
valgrind --tool=dhat ./target/profiling/examples/store-tokens ~/downloads/citylots.json
# ==1146722== Total: 13,823,524 bytes in 196,541 blocks
# ==1146722== At t-gmax: 7,529,044 bytes in 196,515 blocks
valgrind --tool=dhat ./target/profiling/examples/print-tokens ~/downloads/citylots.json
# ==1152944== Total: 1,240,708 bytes in 196,524 blocks
# ==1152944== At t-gmax: 9,367 bytes in 9 blocks
第一个数字(总计)是程序在执行过程中分配的总堆内存量。
第二个数字(At t-gmax)是执行过程中任何时刻分配的最大内存量
不出所料,store-tokens.rs
的内存占用更大。然而,由于该crate的总内存分配(13 MB)仍然比文件大小(189 MB)小一个数量级,因此其实用性仍然明显。
当您可以直接操作产生的令牌时(即,您不累积它们),情况会变得更好。这不仅使您的总分配更少,而且您的内存占用也小得多。print-tokens.rs
在处理文件时,在任何时刻最多使用7KB的堆内存。