Rust 中解析 JSON的方法
Rust 中如何解析 JSON
在本文中,我們將討論如何在 Rust 中使用 JSON 解析庫,以及比較最流行的庫及其性能。
JSON 解析基礎(chǔ)知識
手動解析 JSON
要開始在 Rust 中使用 JSON,您需要安裝一個可以輕松操作 JSON 的庫。目前可用的流行crate之一是 serde-json
。您可以通過運(yùn)行以下命令來安裝它:
cargo add serde-json
完成后,您可以像這樣手動創(chuàng)建 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(()) }
然而,我們可以做得更好。例如,可以將結(jié)構(gòu) 序列化為 JSON 與 或反序列化,這很常用。我們可以在 JSON 模板、Web 服務(wù)、CLI 參數(shù)等中使用它。在下一節(jié)中完成這一點(diǎn)。
使用 Serde 解析 JSON
Serde 是一個crate,可以幫助您將數(shù)據(jù)序列化和反序列化為各種格式,其中一個流行的用途是用于 JSON。如果您使用 Rust 編寫 Web 服務(wù),Serde 是您的朋友,因?yàn)槟鷮⒔?jīng)常處理可能需要發(fā)送或接收的 JSON 數(shù)據(jù)。 Serde 提供了兩個主要traits來幫助您實(shí)現(xiàn)此目的: Serialize
和 Deserialize
。為了方便起見,添加了派生宏實(shí)現(xiàn)來幫助解決此問題。請參閱下文了解如何執(zhí)行此操作:
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(); }
您還可以通過添加實(shí)現(xiàn) Serialize
和 Deserialize
的結(jié)構(gòu)作為另一個也實(shí)現(xiàn) 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 服務(wù)中。例如,當(dāng)您收到對具有 JSON 正文的 API 的 POST 請求時,您通常會將相關(guān)的 Json
類型作為處理函數(shù)參數(shù)傳遞。見下文:
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 字符串轉(zhuǎn)換為結(jié)構(gòu)體的代碼片段之外,您還可以從其字節(jié)表示形式轉(zhuǎn)換為結(jié)構(gòu)體:
let json_as_bytes = b" { \"message\": \"Hello world!\", }"; let my_struct: MyStruct = serde_json::from_slice(json_as_bytes).unwrap();
如果您想將結(jié)構(gòu)作為字節(jié)數(shù)組存儲在某處,然后稍后將其轉(zhuǎn)回結(jié)構(gòu),那么這特別有用!
同樣,您也可以使用 .from_reader()
方法從 JSON IO 流讀取 JSON 并將其轉(zhuǎn)換為結(jié)構(gòu)體。以下是取自 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())); } }
通過這種方式,您可以直接從流中反序列化,而不是在內(nèi)存中添加緩沖。如果您收到大量基于 JSON 的數(shù)據(jù),這可以為您提供很大幫助!
Comparing Rust JSON crates
比較 Rust JSON crates
盡管 serde-json
可能是最受歡迎的 crate,但它絕不是最快的。與此同時,還出現(xiàn)了一些其他 crate,以提高一般 JSON 解析性能。然而,為了換取性能,CPU SIMD 擴(kuò)展要求存在一些注意事項(xiàng)。不安全代碼的使用也有所增加,盡管一般來說,我們已盡最大努力確保代碼可以安全使用。
所有這些 crate 大部分都具有相同的 API。除非另有說明,否則您可以安全地在這些庫之間切換,并期望每個庫中使用大致相同的 JSON 接口。
serde-json
serde-json
是最容易使用的 Rust JSON 庫。它不需要額外的依賴項(xiàng)來使用,并且當(dāng)您需要訪問原始 JSON 值的慣用操作時,通常建議與 serde
一起使用。 serde-json
還支持 no_std
,允許您關(guān)閉默認(rèn)的 std
功能并啟用 alloc
。
就性能而言, serde-json
本身無論如何都不慢。但是,它比此列表中的其他一些 JSON 庫慢。這主要是由于針對 非并行 CPU 使用進(jìn)行了優(yōu)化。特別是如果您能夠訪問現(xiàn)代 x86 CPU,您可能需要繼續(xù)閱讀以了解有關(guān)一些性能更好的選項(xiàng)的更多信息。然而,這個crate 也是 Rust 社區(qū)中使用最廣泛和支持最多的,所以如果您遇到問題,那么很容易找到幫助!
simd-json
simd-json
是 simdjson
C++ JSON 解析器的 Rust 語言綁定,內(nèi)置了 serde
兼容性。顧名思義,該庫使用 SIMD(單指令的縮寫)多個數(shù)據(jù)。這是一種能夠通過并行處理來處理多個數(shù)據(jù)點(diǎn)的技術(shù),使其速度顯著加快!但需要注意的是,它要求您的系統(tǒng)支持 x86,并且在運(yùn)行時它將選擇最佳的 SIMD 功能集以實(shí)現(xiàn)性能。如果沒有可用的功能集,還有一個未優(yōu)化的 Rust 實(shí)現(xiàn),但在文檔中提到不應(yīng)依賴它。
文檔中提到 simd-json
可以在本機(jī)目標(biāo)編譯上滿負(fù)荷使用。您可以通過在運(yùn)行程序時啟用 rustc 中的以下編譯器選項(xiàng)來做到這一點(diǎn),如下所示:
rustc -C target-cpu=native
但是,如果您像大多數(shù)使用 Cargo 的人一樣,您可能想使用 cargo run
。如示例中所示,您可以在 .cargo/config
處創(chuàng)建配置,然后添加以下內(nèi)容:
[build] rustflags = ["-C", "target-cpu=native"]
一般來說,雖然這個庫相當(dāng)快,但應(yīng)該注意的是,由于它是 C++ 的 rust 語言綁定,因此該 crate 中存在相當(dāng)多的不安全代碼。這并不是說你不應(yīng)該使用它,而是要謹(jǐn)慎使用它(正如crate所說)。盡管如此,有一個關(guān)于安全的部分詳細(xì)介紹了如何堅(jiān)持最佳實(shí)踐(如單元測試)以確保crates盡可能安全地使用。
還應(yīng)該提到的是,為了獲得最佳性能,通常最好啟用 jemalloc
或 mimalloc
功能,以便能夠充分利用該庫。
一般來說, simd-json
的 API 與 serde-json
相同,因此如果您想隨時切換,那么通常這樣做不會有任何問題。
sonic-rs
sonic-rs
是具有 SIMD 功能的 JSON 操作的 Rust 實(shí)現(xiàn)。該庫還有 C++ 和 Go 中的對應(yīng)庫!雖然它過去需要 Rust nightly 工具鏈,但它支持穩(wěn)定的 Rust。與 simd-json
類似,它也需要 x86 CPU 架構(gòu)才能滿負(fù)荷運(yùn)行。
與 simd-json
一樣,要使用 sonic-rs
您需要在運(yùn)行程序時在 rustc 中啟用以下編譯器選項(xiàng):
rustc -C target-cpu=native
您可以在 .cargo/config
創(chuàng)建配置,然后添加以下內(nèi)容以在使用 cargo run
時啟用它:
[build] rustflags = ["-C", "target-cpu=native"]
這使您無需執(zhí)行任何其他操作即可構(gòu)建 SIMD!
與 simd-json
一樣,使用了相當(dāng)數(shù)量的 unsafe
代碼。但是,如果您在庫中搜索不安全代碼,您可能會發(fā)現(xiàn)比以前的庫中更多的 unsafe
代碼。關(guān)于如何維護(hù)不安全保證的文檔也很少,因此盡管這個庫可能比 simd-json
更快,但您需要仔細(xì)檢查是否存在未定義的行為!
sonic-rs
另外還有一些額外的方法用于惰性評估和額外的速度。例如,如果您想要 JSON 字符串文字,則可以在反序列化時使用 LazyValue
類型將其轉(zhuǎn)換為仍包含正斜杠的 JSON 字符串值。如果您不怕不安全行為或者確定它不會出錯,還有很多 unchecked
方法可以使用。
雖然 sonic-rs
是一個相當(dāng)快的庫,但它也是一個更新的 crate,因此 crate 中缺少一些方法,例如 from_reader
(允許從 IO 流讀?。?。這已經(jīng)作為 GitHub 問題提出,所以希望它能盡快實(shí)施。
基準(zhǔn)
您可以在此處找到 simd-json
和 serde-json
的基準(zhǔn)。 simd-json
與 serde-json
相比有相當(dāng)顯著的改進(jìn)。
您可以在此處找到 sonic-rs
的基準(zhǔn),其中還將其與 simd-json
和 serde-json
進(jìn)行比較。正如您所看到的,最終結(jié)果的格式與 simd-json
和 serde-json
基準(zhǔn)測試的格式不同,因此每秒處理的數(shù)據(jù)量更難以理解。然而,在大多數(shù)情況下, sonic-rs
明顯(有時是巨大?。┍?simd-json
和 serde-json
快。
尾聲
謝謝閱讀!我希望本文能夠幫助您了解如何有效使用 Rust JSON 解析庫。
原文地址:Parsing JSON in Rust
到此這篇關(guān)于Rust 中如何解析 JSON?的文章就介紹到這了,更多相關(guān)Rust 解析 JSON內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust生命周期之驗(yàn)證引用有效性與防止懸垂引用方式
本文介紹了Rust中生命周期注解的應(yīng)用,包括防止懸垂引用、在函數(shù)中使用泛型生命周期、生命周期省略規(guī)則、在結(jié)構(gòu)體中使用生命周期、靜態(tài)生命周期以及如何將生命周期與泛型和特質(zhì)約束結(jié)合,通過這些機(jī)制,Rust在編譯時就能捕獲內(nèi)存安全問題2025-02-02C和Java沒那么香了,Serverless時代Rust即將稱王?
Serverless Computing,即”無服務(wù)器計(jì)算”,其實(shí)這一概念在剛剛提出的時候并沒有獲得太多的關(guān)注,直到2014年AWS Lambda這一里程碑式的產(chǎn)品出現(xiàn)。Serverless算是正式走進(jìn)了云計(jì)算的舞臺2021-06-06