Rust 中解析 JSON的方法
作者:xuejianxinokok
Rust 中如何解析 JSON
在本文中,我们将讨论如何在 Rust 中使用 JSON 解析库,以及比较最流行的库及其性能。
JSON 解析基础知识
手动解析 JSON
要开始在 Rust 中使用 JSON,您需要安装一个可以轻松操作 JSON 的库。目前可用的流行crate之一是 serde-json
。您可以通过运行以下命令来安装它:
cargo add serde-json
完成后,您可以像这样手动创建 JSON:
use serde_json::{Result, Value}; fn untyped_example() -> Result<()> { // Some JSON input data as a &str. Maybe this comes from the user. let data = r#" {"name":"John Doe", "age": 43, "phones": ["+44 1234567", "+44 2345678" ] }"#; // Parse the string of data into serde_json::Value. let v: Value = serde_json::from_str(data)?; // Access parts of the data by indexing with square brackets. println!("Please call {} at the number {}", v["name"], v["phones"][0]); Ok(()) }
然而,我们可以做得更好。例如,可以将结构 序列化为 JSON 与 或反序列化,这很常用。我们可以在 JSON 模板、Web 服务、CLI 参数等中使用它。在下一节中完成这一点。
使用 Serde 解析 JSON
Serde 是一个crate,可以帮助您将数据序列化和反序列化为各种格式,其中一个流行的用途是用于 JSON。如果您使用 Rust 编写 Web 服务,Serde 是您的朋友,因为您将经常处理可能需要发送或接收的 JSON 数据。 Serde 提供了两个主要traits来帮助您实现此目的: Serialize
和 Deserialize
。为了方便起见,添加了派生宏实现来帮助解决此问题。请参阅下文了解如何执行此操作:
use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct MyStruct { message: String } fn convert_json_to_struct() { // create a raw JSON string from the json! macro and turn it into a MyStruct struct let raw_json_string = json!({"message": "Hello world!"}); let my_struct: MyStruct = serde_json::from_str(raw_json_string).unwrap(); }
您还可以通过添加实现 Serialize
和 Deserialize
的结构作为另一个也实现 Serialize
和 Deserialize
:
use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct Post { nested_json: PostMetadata, title: String, body: String } #[derive(Serialize, Deserialize)] pub struct PostMetadata { timestamp_created: DateTime<Utc>, timestamp_last_updated: Datetime<Utc>, categories: Vec<String>, }
一种用例是将 JSON 嵌套在 Web 服务中。例如,当您收到对具有 JSON 正文的 API 的 POST 请求时,您通常会将相关的 Json
类型作为处理函数参数传递。见下文:
use axum::Json; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct Post { nested_json: PostMetadata, title: String, body: String } #[derive(Serialize, Deserialize)] pub struct PostMetadata { timestamp_created: DateTime<Utc>, timestamp_last_updated: Datetime<Utc>, categories: Vec<String>, } async fn receive_some_json( // this extractor consumes a JSON body and converts it into the struct type given Json(json): Json<Post> ) -> Json<Post> { println!("{:?}", json); Json(json) }
除了前面显示如何使用 serde_json
从 JSON 字符串转换为结构体的代码片段之外,您还可以从其字节表示形式转换为结构体:
let json_as_bytes = b" { \"message\": \"Hello world!\", }"; let my_struct: MyStruct = serde_json::from_slice(json_as_bytes).unwrap();
如果您想将结构作为字节数组存储在某处,然后稍后将其转回结构,那么这特别有用!
同样,您也可以使用 .from_reader()
方法从 JSON IO 流读取 JSON 并将其转换为结构体。以下是取自 serde_json
文档的示例,说明如何将其与 TCP 流一起使用:
use serde::Deserialize; use std::error::Error; use std::net::{TcpListener, TcpStream}; #[derive(Deserialize, Debug)] struct User { fingerprint: String, location: String, } fn read_user_from_stream(tcp_stream: TcpStream) -> Result<User, Box<dyn Error>> { let mut to_be_deserialized = serde_json::Deserializer::from_reader(tcp_stream); let user = User::deserialize(&mut to_be_deserialized)?; Ok(user) } fn main() { let listener = TcpListener::bind("127.0.0.1:4000").unwrap(); for stream in listener.incoming() { println!("{:#?}", read_user_from_stream(stream.unwrap())); } }
通过这种方式,您可以直接从流中反序列化,而不是在内存中添加缓冲。如果您收到大量基于 JSON 的数据,这可以为您提供很大帮助!
Comparing Rust JSON crates
比较 Rust JSON crates
尽管 serde-json
可能是最受欢迎的 crate,但它绝不是最快的。与此同时,还出现了一些其他 crate,以提高一般 JSON 解析性能。然而,为了换取性能,CPU SIMD 扩展要求存在一些注意事项。不安全代码的使用也有所增加,尽管一般来说,我们已尽最大努力确保代码可以安全使用。
所有这些 crate 大部分都具有相同的 API。除非另有说明,否则您可以安全地在这些库之间切换,并期望每个库中使用大致相同的 JSON 接口。
serde-json
serde-json
是最容易使用的 Rust JSON 库。它不需要额外的依赖项来使用,并且当您需要访问原始 JSON 值的惯用操作时,通常建议与 serde
一起使用。 serde-json
还支持 no_std
,允许您关闭默认的 std
功能并启用 alloc
。
就性能而言, serde-json
本身无论如何都不慢。但是,它比此列表中的其他一些 JSON 库慢。这主要是由于针对 非并行 CPU 使用进行了优化。特别是如果您能够访问现代 x86 CPU,您可能需要继续阅读以了解有关一些性能更好的选项的更多信息。然而,这个crate 也是 Rust 社区中使用最广泛和支持最多的,所以如果您遇到问题,那么很容易找到帮助!
simd-json
simd-json
是 simdjson
C++ JSON 解析器的 Rust 语言绑定,内置了 serde
兼容性。顾名思义,该库使用 SIMD(单指令的缩写)多个数据。这是一种能够通过并行处理来处理多个数据点的技术,使其速度显着加快!但需要注意的是,它要求您的系统支持 x86,并且在运行时它将选择最佳的 SIMD 功能集以实现性能。如果没有可用的功能集,还有一个未优化的 Rust 实现,但在文档中提到不应依赖它。
文档中提到 simd-json
可以在本机目标编译上满负荷使用。您可以通过在运行程序时启用 rustc 中的以下编译器选项来做到这一点,如下所示:
rustc -C target-cpu=native
但是,如果您像大多数使用 Cargo 的人一样,您可能想使用 cargo run
。如示例中所示,您可以在 .cargo/config
处创建配置,然后添加以下内容:
[build] rustflags = ["-C", "target-cpu=native"]
一般来说,虽然这个库相当快,但应该注意的是,由于它是 C++ 的 rust 语言绑定,因此该 crate 中存在相当多的不安全代码。这并不是说你不应该使用它,而是要谨慎使用它(正如crate所说)。尽管如此,有一个关于安全的部分详细介绍了如何坚持最佳实践(如单元测试)以确保crates尽可能安全地使用。
还应该提到的是,为了获得最佳性能,通常最好启用 jemalloc
或 mimalloc
功能,以便能够充分利用该库。
一般来说, simd-json
的 API 与 serde-json
相同,因此如果您想随时切换,那么通常这样做不会有任何问题。
sonic-rs
sonic-rs
是具有 SIMD 功能的 JSON 操作的 Rust 实现。该库还有 C++ 和 Go 中的对应库!虽然它过去需要 Rust nightly 工具链,但它支持稳定的 Rust。与 simd-json
类似,它也需要 x86 CPU 架构才能满负荷运行。
与 simd-json
一样,要使用 sonic-rs
您需要在运行程序时在 rustc 中启用以下编译器选项:
rustc -C target-cpu=native
您可以在 .cargo/config
创建配置,然后添加以下内容以在使用 cargo run
时启用它:
[build] rustflags = ["-C", "target-cpu=native"]
这使您无需执行任何其他操作即可构建 SIMD!
与 simd-json
一样,使用了相当数量的 unsafe
代码。但是,如果您在库中搜索不安全代码,您可能会发现比以前的库中更多的 unsafe
代码。关于如何维护不安全保证的文档也很少,因此尽管这个库可能比 simd-json
更快,但您需要仔细检查是否存在未定义的行为!
sonic-rs
另外还有一些额外的方法用于惰性评估和额外的速度。例如,如果您想要 JSON 字符串文字,则可以在反序列化时使用 LazyValue
类型将其转换为仍包含正斜杠的 JSON 字符串值。如果您不怕不安全行为或者确定它不会出错,还有很多 unchecked
方法可以使用。
虽然 sonic-rs
是一个相当快的库,但它也是一个更新的 crate,因此 crate 中缺少一些方法,例如 from_reader
(允许从 IO 流读取)。这已经作为 GitHub 问题提出,所以希望它能尽快实施。
基准
您可以在此处找到 simd-json
和 serde-json
的基准。 simd-json
与 serde-json
相比有相当显着的改进。
您可以在此处找到 sonic-rs
的基准,其中还将其与 simd-json
和 serde-json
进行比较。正如您所看到的,最终结果的格式与 simd-json
和 serde-json
基准测试的格式不同,因此每秒处理的数据量更难以理解。然而,在大多数情况下, sonic-rs
明显(有时是巨大!)比 simd-json
和 serde-json
快。
尾声
谢谢阅读!我希望本文能够帮助您了解如何有效使用 Rust JSON 解析库。
原文地址:Parsing JSON in Rust
到此这篇关于Rust 中如何解析 JSON?的文章就介绍到这了,更多相关Rust 解析 JSON内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!