Rust 中解析 JSON的方法
Rust 中如何解析 JSON

在本文中,我們將討論如何在 Rust 中使用 JSON 解析庫(kù),以及比較最流行的庫(kù)及其性能。
JSON 解析基礎(chǔ)知識(shí)
手動(dòng)解析 JSON
要開(kāi)始在 Rust 中使用 JSON,您需要安裝一個(gè)可以輕松操作 JSON 的庫(kù)。目前可用的流行crate之一是 serde-json 。您可以通過(guò)運(yùn)行以下命令來(lái)安裝它:
cargo add serde-json
完成后,您可以像這樣手動(dòng)創(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 是一個(gè)crate,可以幫助您將數(shù)據(jù)序列化和反序列化為各種格式,其中一個(gè)流行的用途是用于 JSON。如果您使用 Rust 編寫(xiě) Web 服務(wù),Serde 是您的朋友,因?yàn)槟鷮⒔?jīng)常處理可能需要發(fā)送或接收的 JSON 數(shù)據(jù)。 Serde 提供了兩個(gè)主要traits來(lái)幫助您實(shí)現(xiàn)此目的: Serialize 和 Deserialize 。為了方便起見(jiàn),添加了派生宏實(shí)現(xiàn)來(lái)幫助解決此問(wèn)題。請(qǐng)參閱下文了解如何執(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();
}您還可以通過(guò)添加實(shí)現(xiàn) Serialize 和 Deserialize 的結(jié)構(gòu)作為另一個(gè)也實(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)您收到對(duì)具有 JSON 正文的 API 的 POST 請(qǐng)求時(shí),您通常會(huì)將相關(guān)的 Json 類(lèi)型作為處理函數(shù)參數(shù)傳遞。見(jiàn)下文:
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ù)組存儲(chǔ)在某處,然后稍后將其轉(zhuǎn)回結(jié)構(gòu),那么這特別有用!
同樣,您也可以使用 .from_reader() 方法從 JSON IO 流讀取 JSON 并將其轉(zhuǎn)換為結(jié)構(gòu)體。以下是取自 serde_json 文檔的示例,說(shuō)明如何將其與 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()));
}
}通過(guò)這種方式,您可以直接從流中反序列化,而不是在內(nèi)存中添加緩沖。如果您收到大量基于 JSON 的數(shù)據(jù),這可以為您提供很大幫助!
Comparing Rust JSON crates
比較 Rust JSON crates
盡管 serde-json 可能是最受歡迎的 crate,但它絕不是最快的。與此同時(shí),還出現(xiàn)了一些其他 crate,以提高一般 JSON 解析性能。然而,為了換取性能,CPU SIMD 擴(kuò)展要求存在一些注意事項(xiàng)。不安全代碼的使用也有所增加,盡管一般來(lái)說(shuō),我們已盡最大努力確保代碼可以安全使用。
所有這些 crate 大部分都具有相同的 API。除非另有說(shuō)明,否則您可以安全地在這些庫(kù)之間切換,并期望每個(gè)庫(kù)中使用大致相同的 JSON 接口。
serde-json
serde-json 是最容易使用的 Rust JSON 庫(kù)。它不需要額外的依賴(lài)項(xiàng)來(lái)使用,并且當(dāng)您需要訪(fǎng)問(wèn)原始 JSON 值的慣用操作時(shí),通常建議與 serde 一起使用。 serde-json 還支持 no_std ,允許您關(guān)閉默認(rèn)的 std 功能并啟用 alloc 。
就性能而言, serde-json 本身無(wú)論如何都不慢。但是,它比此列表中的其他一些 JSON 庫(kù)慢。這主要是由于針對(duì) 非并行 CPU 使用進(jìn)行了優(yōu)化。特別是如果您能夠訪(fǎng)問(wèn)現(xiàn)代 x86 CPU,您可能需要繼續(xù)閱讀以了解有關(guān)一些性能更好的選項(xiàng)的更多信息。然而,這個(gè)crate 也是 Rust 社區(qū)中使用最廣泛和支持最多的,所以如果您遇到問(wèn)題,那么很容易找到幫助!
simd-json
simd-json 是 simdjson C++ JSON 解析器的 Rust 語(yǔ)言綁定,內(nèi)置了 serde 兼容性。顧名思義,該庫(kù)使用 SIMD(單指令的縮寫(xiě))多個(gè)數(shù)據(jù)。這是一種能夠通過(guò)并行處理來(lái)處理多個(gè)數(shù)據(jù)點(diǎn)的技術(shù),使其速度顯著加快!但需要注意的是,它要求您的系統(tǒng)支持 x86,并且在運(yùn)行時(shí)它將選擇最佳的 SIMD 功能集以實(shí)現(xiàn)性能。如果沒(méi)有可用的功能集,還有一個(gè)未優(yōu)化的 Rust 實(shí)現(xiàn),但在文檔中提到不應(yīng)依賴(lài)它。
文檔中提到 simd-json 可以在本機(jī)目標(biāo)編譯上滿(mǎn)負(fù)荷使用。您可以通過(guò)在運(yùn)行程序時(shí)啟用 rustc 中的以下編譯器選項(xiàng)來(lái)做到這一點(diǎn),如下所示:
rustc -C target-cpu=native
但是,如果您像大多數(shù)使用 Cargo 的人一樣,您可能想使用 cargo run 。如示例中所示,您可以在 .cargo/config 處創(chuàng)建配置,然后添加以下內(nèi)容:
[build] rustflags = ["-C", "target-cpu=native"]
一般來(lái)說(shuō),雖然這個(gè)庫(kù)相當(dāng)快,但應(yīng)該注意的是,由于它是 C++ 的 rust 語(yǔ)言綁定,因此該 crate 中存在相當(dāng)多的不安全代碼。這并不是說(shuō)你不應(yīng)該使用它,而是要謹(jǐn)慎使用它(正如crate所說(shuō))。盡管如此,有一個(gè)關(guān)于安全的部分詳細(xì)介紹了如何堅(jiān)持最佳實(shí)踐(如單元測(cè)試)以確保crates盡可能安全地使用。
還應(yīng)該提到的是,為了獲得最佳性能,通常最好啟用 jemalloc 或 mimalloc 功能,以便能夠充分利用該庫(kù)。
一般來(lái)說(shuō), simd-json 的 API 與 serde-json 相同,因此如果您想隨時(shí)切換,那么通常這樣做不會(huì)有任何問(wèn)題。
sonic-rs
sonic-rs 是具有 SIMD 功能的 JSON 操作的 Rust 實(shí)現(xiàn)。該庫(kù)還有 C++ 和 Go 中的對(duì)應(yīng)庫(kù)!雖然它過(guò)去需要 Rust nightly 工具鏈,但它支持穩(wěn)定的 Rust。與 simd-json 類(lèi)似,它也需要 x86 CPU 架構(gòu)才能滿(mǎn)負(fù)荷運(yùn)行。
與 simd-json 一樣,要使用 sonic-rs 您需要在運(yùn)行程序時(shí)在 rustc 中啟用以下編譯器選項(xiàng):
rustc -C target-cpu=native
您可以在 .cargo/config 創(chuàng)建配置,然后添加以下內(nèi)容以在使用 cargo run 時(shí)啟用它:
[build] rustflags = ["-C", "target-cpu=native"]
這使您無(wú)需執(zhí)行任何其他操作即可構(gòu)建 SIMD!
與 simd-json 一樣,使用了相當(dāng)數(shù)量的 unsafe 代碼。但是,如果您在庫(kù)中搜索不安全代碼,您可能會(huì)發(fā)現(xiàn)比以前的庫(kù)中更多的 unsafe 代碼。關(guān)于如何維護(hù)不安全保證的文檔也很少,因此盡管這個(gè)庫(kù)可能比 simd-json 更快,但您需要仔細(xì)檢查是否存在未定義的行為!
sonic-rs 另外還有一些額外的方法用于惰性評(píng)估和額外的速度。例如,如果您想要 JSON 字符串文字,則可以在反序列化時(shí)使用 LazyValue 類(lèi)型將其轉(zhuǎn)換為仍包含正斜杠的 JSON 字符串值。如果您不怕不安全行為或者確定它不會(huì)出錯(cuò),還有很多 unchecked 方法可以使用。
雖然 sonic-rs 是一個(gè)相當(dāng)快的庫(kù),但它也是一個(gè)更新的 crate,因此 crate 中缺少一些方法,例如 from_reader (允許從 IO 流讀?。?。這已經(jīng)作為 GitHub 問(wèn)題提出,所以希望它能盡快實(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)測(cè)試的格式不同,因此每秒處理的數(shù)據(jù)量更難以理解。然而,在大多數(shù)情況下, sonic-rs 明顯(有時(shí)是巨大?。┍?simd-json 和 serde-json 快。
尾聲
謝謝閱讀!我希望本文能夠幫助您了解如何有效使用 Rust JSON 解析庫(kù)。
原文地址:Parsing JSON in Rust
到此這篇關(guān)于Rust 中如何解析 JSON?的文章就介紹到這了,更多相關(guān)Rust 解析 JSON內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust 累計(jì)時(shí)間長(zhǎng)度的操作方法
在Rust中,如果你想要記錄累計(jì)時(shí)間,通??梢允褂脴?biāo)準(zhǔn)庫(kù)中的std::time::Duration類(lèi)型,這篇文章主要介紹了Rust如何累計(jì)時(shí)間長(zhǎng)度,需要的朋友可以參考下2024-05-05
Rust用宏實(shí)現(xiàn)參數(shù)可變的函數(shù)的實(shí)現(xiàn)示例
本文主要介紹了Rust用宏實(shí)現(xiàn)參數(shù)可變的函數(shù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-03-03
在win10上使用mingw64編譯器配置Rust開(kāi)發(fā)環(huán)境和idea 配置Rust 插件
在win10上配置 Rust 開(kāi)發(fā)環(huán)境(使用 mingw64編譯器)和 idea 配置 Rust 插件的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-03-03
Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的操作方法
Moka是一個(gè)用于Rust的高性能緩存庫(kù),它提供了多種類(lèi)型的緩存數(shù)據(jù)結(jié)構(gòu),包括哈希表、LRU(最近最少使用)緩存和?支持TTL(生存時(shí)間)緩存,這篇文章給大家介紹Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的相關(guān)知識(shí),感興趣的朋友一起看看吧2024-01-01
使用cargo install安裝Rust二進(jìn)制工具過(guò)程
cargoinstall是一個(gè)用于安裝包含可執(zhí)行目標(biāo)的Rust包的命令行工具,類(lèi)似于系統(tǒng)軟件包管理器,但它為Rust開(kāi)發(fā)者提供了一種簡(jiǎn)潔的方式來(lái)安裝和管理命令行工具,安裝后,二進(jìn)制文件會(huì)存儲(chǔ)在$HOME/.cargo/bin目錄中,需要將該目錄添加到$PATH環(huán)境變量中才能在命令行中直接運(yùn)行2025-02-02
關(guān)于使用rust調(diào)用c++靜態(tài)庫(kù)并編譯nodejs包的問(wèn)題
這篇文章主要介紹了使用rust調(diào)用c++靜態(tài)庫(kù)并編譯nodejs包的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
用rust?寫(xiě)一個(gè)jar包?class沖突檢測(cè)工具
這篇文章主要介紹了用rust?寫(xiě)一個(gè)jar包?class沖突檢測(cè)工具?的相關(guān)資料,需要的朋友可以參考下2023-05-05

