如何在Rust中操作JSON

越努力,越幸运

大家好,我是「柒八九」。一个「专注于前端开发技术/RustAI应用知识分享」Coder

前言

我们之前在Rust 赋能前端-开发一款属于你的前端脚手架中有过在Rust项目中如何操作JSON

alt

由于文章篇幅的原因,我们就没详细介绍这块的内容,而今天我们就抽空聊聊这个话题。 -- 「如何在Rust中操作JSON,以及对最流行的库进行比较」

好了,天不早了,干点正事哇。

alt

我们能所学到的知识点

  1. 操作JSON数据
  2. 比较 Rust 的 JSON crates

1. 操作JSON数据

创建JSON数据

要在Rust中处理JSON,我们可以借助相关的JSON库。其实市面上有很多相关的库,但是我们还是选择一种我们比较熟悉并且流行度高的库。--serde-json[1]

我们可以通过运行以下命令来安装它:

cargo add serde-json

完成后,我们可以像这样手动创建JSON

use serde_json::{Result, Value};

fn untyped_example() -> Result<()> {
    // 一些JSON输入数据,作为一个&str。也许这些数据来自用户。
    let data = r#"
        {
            "name": "Front789",
            "age": 18,
            "ability": [
                "Front-end development",
                "Rust",
                "AI"
            ]
        }"#
;

    // 将数据字符串解析为serde_json::Value。
    let v: Value = serde_json::from_str(data)?;

    // 通过使用方括号索引来访问数据的部分。
    println!("我是{}。一个专注于{}/{}及{}应用知识分享**的Coder"
    v["name"], v["ability"][0],v["ability"][1],v["ability"][2]);

    Ok(())
}

然而,我们可以做得比这更好。例如,我们可以将JSON序列化为结构体,这在许多应用中都有用途。我们可以在JSON模板Web服务CLI参数(这点我们的f_cli[2]就使用了它)等方面使用它。

当然,我们也可以使用std::fs::write来将这些JSON数据写入到磁盘文件中。

使用Serde解析JSON

Serde是一个crate,它帮助我们将数据序列化和反序列化为各种格式,其中一个流行的用途是用于JSONSerde提供了两个主要的trait来帮助我们完成这一点:SerializeDeserialize。我们可以添加了一个派生宏实现来帮助我们完成这一点。

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct MyStruct {
    message: String
}

fn convert_json_to_struct() {
    // 从json!宏创建一个原始的JSON字符串,并将其转换为MyStruct结构体
    let raw_json_string = json!({"message""Hello Front789!"});
    let my_struct: MyStruct = serde_json::from_str(raw_json_string).unwrap();
}

我们还可以创建「嵌套的JSON」,方法是将实现SerializeDeserialize的结构体作为另一个也实现SerializeDeserialize的结构体的字段:

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>,
}

上面的代码可以用于我们用Rust创建一个Web服务(还记得我们之前介绍过的Rust Web 开发之Axum使用手册吗),并且返回一个嵌套JSON。例如,当我们的Web服务器收到一个POST请求,其Body中是一个Json数据时,我们通常会将相关的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(
  // 这个提取器消耗一个JSON主体,并将其转换为给定的结构类型
    Json(json): Json<Post>
) -> Json<Post> {
    println!("{:?}", json);
    Json(json)
}

我们还可以从其字节表示形式转换为结构体:

let json_as_bytes = b"
        {
            \"message\": \"Hello Front789!\",
        }"
;

    let my_struct: MyStruct = serde_json::from_slice(json_as_bytes).unwrap();

上面的处理方式,在我们想将一个结构体存储在某个地方作为字节数组,然后再将其转换回结构体时,有奇特的效果!

类似地,我们还可以从JSON「IO流」中读取JSON并将其转换为结构体,使用.from_reader()方法。以下代码中展示了如何在TCP流中使用它:

use serde::Deserialize;
use std::error::Error;
use std::net::{TcpListener, TcpStream};

#[derive(Deserialize, Debug)]
struct User {
    name: String,
    age: 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:7890").unwrap();

    for stream in listener.incoming() {
        println!("{:#?}", read_user_from_stream(stream.unwrap()));
    }
}

这样,当我们在遇到需要处理JSON的数据时,我们就可以直接从流中反序列化,而不是在内存中添加缓冲区。


2. 比较 Rust 的 JSON crates

其实,在大部分情况下,serde-json已经能够满足我们的需求了。但是,在一些特殊情况下,例如数据量过大,此时serde-json就有点吃力了。所以,市面上又有了一些提高 JSON 解析性能的crate。(simd-json/sonic-rs)

alt

从上图可知serde-json有碾压式优势,也就是不到万不得已,我们还是使用serde-json。不过,本着知己知彼,方能百战不殆。我们也需要知晓额外的解决方案。

这些 crates 大部分具有相同的 API。除非另有说明,否则我们可以安全地在这些库之间切换,并期望在每个库中使用 JSON 时具有大致相同的接口。

serde-json

serde-jsonRust 中下载和使用最多的 JSON 库之一。

就性能而言,serde-json 本身并不慢。然而,然后对比其他两个crate就有点稍逊了。这主要是因为它被采用非并行化的 CPU 使用架构。这样的话,serde-json就无法在x86 CPU的系统架构上,发挥更强的作用。

x86 是一种广泛使用的中央处理单元 (CPU) 计算机架构。它已成为个人计算机和服务器的主导架构。 x86这个名称源自 8086,这是英特尔® 发布的早期处理器。 x86 CPU 使用「复杂指令集计算机」 (CISC) 设计,允许它们在「单个周期内执行多条指令」。x想了解更多关于x86 CPU的内容,可以参考x86介绍[3]

simd-json

simd-json[4]simdjson C++ JSON 解析器的 Rust 版本,内置了 serde 兼容性。正如其名称所示,此库使用 SIMD(单指令多数据)。这是一种用于能够使用并行处理处理多个数据点的技术,使其速度显著更快!然而,作为一个注意事项,它要求我们的系统具有 x86 能力,并且在运行时会选择最佳的 SIMD 特性集以获得性能。

文档中提到 simd-json 可以在本机目标编译时充分发挥作用。我们可以通过在运行程序时启用 rustc 中的以下编译器选项来实现此目标,例如:

rustc -C target-cpu=native

然而,如果我们像大多数使用 Cargo 的人一样,我们可能想使用 cargo run。与示例中一样,我们可以在 .cargo/config 中创建一个配置,然后添加以下内容:

[build]
rustflags = ["-C""target-cpu=native"]

.cargo/config配置相关的内容,我们在Rust交叉编译Windows环境时候,也涉及到。

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

一般来说,尽管这个库非常快,但应该注意到这个 crate 中有相当多的不安全代码,因为它是 C++ crate 的一个移植。这并不意味着我们不应该使用它,而是要谨慎使用。

还应该提到的是,为了获得最佳性能,通常最好启用 jemallocmimalloc 特性,以充分利用库。

通常情况下,simd-json 的 API 与 serde-json 相同,因此如果我们想在任何时候切换,通常不应该遇到任何问题。

sonic-rs

sonic-rs[5] 是具有 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 类似,这个库中使用了相当多的不安全代码。然而,如果我们在库中搜索不安全代码,我们会发现比之前的库中的不安全代码可能更多。

sonic-rs 还具有一些额外的方法来进行惰性评估和提高速度。例如,如果我们想要一个 JSON 字符串文字,我们可以在反序列化时使用 LazyValue 类型将其转换为一个仍然带有斜杠的 JSON 字符串值。如果我们不怕不安全行为,或者确信它不会出错,还有很多未经检查的方法可供我们使用。

尽管 sonic-rs 是一个非常快的库,但它也是一个较新的 crate,因此某些方法,如 from_reader(允许从 IO 流读取)在 crate 中缺失。


后记

「分享是一种态度」

「全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。」

alt

Reference

[1]

serde-json: https://crates.io/crates/serde_json

[2]

f_cli: https://www.npmjs.com/package/f_cli_f

[3]

x86介绍: https://www.lenovo.com/us/en/glossary/x86/

[4]

simd-json: https://crates.io/crates/simd-json

[5]

sonic-rs: https://crates.io/crates/sonic-rs

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/527323.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT 使用redis ,连接并使用

一.redis安装 链接&#xff1a;https://pan.baidu.com/s/17fXKOj5M4VIypR0y5_xtHw 提取码&#xff1a;1234 1.下载得到文件夹如图 course_redis为安装包。 2.启动Redis服务 把安装包解压到某个路径下即可。 打开cmd窗口&#xff0c;切换到Redis安装路径&#xff0c;输入 r…

微服务初始及Eureka注册中心

1&#xff0c;架构演变 单体架构&#xff1a;将所有业务功能集中在一个项目中开发&#xff0c;达成一个包部署 优点&#xff1a;架构简单&#xff0c;部署成本低 缺点&#xff1a;项目耦合度高 分布式架构&#xff1a;根据业务功能对系统进行拆分&#xff0c;每个业务作为独…

Codeforces Round 938 (Div. 3) A - F 题解

A. Yogurt Sale 题意&#xff1a;要购买n个酸奶&#xff0c;有两种买法&#xff0c;一种是一次买一个&#xff0c;价格a。一种是一次买两个&#xff0c;价格b&#xff0c;问买n个酸奶的最小价格。 题解&#xff1a;很容易想到用2a和b比较&#xff0c;判断输出即可。 代码&am…

小程序开发SSL证书下载和安装

在开发小程序时&#xff0c;确保数据的安全传输至关重要&#xff0c;而实现这一目标的关键在于正确获取与安装SSL证书。以下详细介绍了从获取到安装SSL证书的完整流程&#xff0c;以助您为小程序构建可靠的加密通信环境。 一、小程序SSL证书类型选择&#xff1a; 域名验证型D…

【PyTorch][chapter 25][李宏毅深度学习][Transfer Learning-1]

前言&#xff1a; 迁移学习是一种机器学习的方法,指的是一个预训练的模型被重新用在另一个任务中。 比如已经有个模型A 实现了猫狗分类 模型B 要实现大象和老虎分类,可以利用训练好的模型A 的一些参数特征,简化当前的训练 过程. 目录&#xff1a; 简介 Model Fine-Tuning (…

【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)

目录 前言 什么是面向对象&#xff1f; 类的定义 类的访问限定符及封装 访问限定符 封装 类的作用域 类的实例化 类对象的存储方式 this指针 结语 前言 最早的C版本&#xff08;C with classes&#xff09;中&#xff0c;最先加上的就是类的机制&#xff0c;它构成…

Day3-HBase重要概念

HBase 结构 HRegion 概述 在HBase中&#xff0c;会从行键方向上对表来进行切分&#xff0c;切分出来的每一个结构称之为是一个HRegion 切分之后&#xff0c;每一个HRegion会交给某一个HRegionServer来进行管理。HRegionServer是HBase的从节点&#xff0c;每一个HRegionServ…

如何使用Java和RabbitMQ实现延迟队列(方式二)?

前言 昨天写了一篇关于Java和RabbitMQ使用插件实现延迟队列功能的文章&#xff0c;今天来讲下另外一种方式&#xff0c;不需要RabbitMQ的插件。 前期准备&#xff0c;需要安装好docker、docker-compose的运行环境。 需要安装RabbitMQ的可以看下面这篇文章。 如何使用PHP和R…

Spring Cloud微服务入门(四)

应用容错的概念 应用错误-雪崩效应 定义&#xff1a; 服务雪崩效应是一种因“服务提供者的不可用”&#xff08;原因&#xff09;导致“服务调用者不可用”&#xff08;结果&#xff09;&#xff0c;并将不可用逐渐放大的现象。 服务雪崩的过程可以分为三个阶段&#xff1a; 服…

Excel全套213集教程

Excel全套213集教程 包含技术入门93集 图表17集 数据透视35集 公式函数68 基础入门 93节 https://www.alipan.com/s/cMxuPstkS1x 提取码: 77dd 点击链接保存&#xff0c;或者复制本段内容&#xff0c;打开「阿里云盘」APP &#xff0c;无需下载极速在线查看&#xff0c;视…

电商社交新零售:创新引领新趋势,变革新零售思维格局-亿发

新零售O2O模式是如何颠覆传统零售商业模式&#xff1f; 传统电商出现瓶颈&#xff1a; 传统电商在发展过程中逐渐出现了瓶颈&#xff0c;主要表现在市场竞争激烈、用户获取成本上升、用户黏性下降等问题。传统电商往往只能通过价格竞争或促销活动来吸引用户&#xff0c;而这种…

SSL、TLS和HTTPS:网络安全的重要基石

随着互联网的快速发展&#xff0c;网络安全问题日益凸显。为了保护数据在传输过程中的安全&#xff0c;各种加密协议和技术应运而生。SSL&#xff08;安全套接层&#xff09;、TLS&#xff08;传输层安全&#xff09;和HTTPS&#xff08;超文本传输安全协议&#xff09;是三个最…

超级agent的端语言模型Octopus v2: On-device language model for super agent

大型语言模型&#xff08;LLMs&#xff09;在函数调用方面展现出卓越的应用潜力&#xff0c;特别是针对Android API的定制应用。与那些需要详尽描述潜在函数参数、有时甚至涉及数万个输入标记的检索增强生成&#xff08;RAG&#xff09;方法相比&#xff0c;Octopus-V2-2B在训练…

uniapp引入微信小程序版本VantUI,使用VantUI的自定义tabbar,并解决自定义tabbar出现闪烁的情况

1.uniapp引入微信小程序版本VantUI 去vant官网下载源码&#xff0c;源码放在github&#xff0c;自行去下载下来 https://vant-contrib.gitee.io/vant-weapp/#/home 在pages.json的globalStyle里面注册组件 "globalStyle": {"navigationBarTextStyle": &qu…

gitlab使用

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 目录 一&#xff1a…

【xcode15.3 打包报错 Command SwiftCompile failed with a nonzero exit code】

升级Xcode15后 打包报错 xxx Command SwiftCompile failed with a nonzero exit code 解决办法&#xff1a; 选中pod 报错的库 Code Generation->Compilation Mode改成和debug一样的 Incremental。

智慧水库解决方案(打造水库智慧监测体系)

​作为一名水利自动化系统集成商,最近我司接手了一个智慧水库建设项目。这个项目位于一座山区的大型水库,目的是对其进行现代化、智能化改造,提升供水、防洪等管理水平。&#xff08;key-iot.com.cn&#xff09; 在方案设计之初,我们组织了现场勘测,全面了解水库的实际情况。这…

完全可定制的富文本编辑器:逻辑清晰,插件赋能 | 开源日报 No.218

ianstormtaylor/slate Stars: 28.8k License: MIT slate 是一个完全可定制的框架&#xff0c;用于构建富文本编辑器。 可以构建类似 Medium、Dropbox Paper 或 Google Docs 的富文本编辑器通过一系列插件实现所有逻辑&#xff0c;避免代码复杂度受到 Draft.js、Prosemirror 和…

最少按键次数

题目描述 给你一个字符串 s&#xff0c;由小写英文字母组成。 电话键盘上的按键与 不同 小写英文字母集合相映射&#xff0c;可以通过按压按键来组成单词。例如&#xff0c;按键 2 对应 ["a","b","c"]&#xff0c;我们需要按一次键来输入 &quo…

web前端框架设计第四课-条件判断与列表渲染

web前端框架设计第四课-条件判断与列表渲染 一.预习笔记 1.条件判断 1-1&#xff1a;v-if指令&#xff1a;根据表达式的值来判断是否输出DOM元素 1-2&#xff1a;template中使用v-if 1-3&#xff1a;v-else 1-4&#xff1a;v-else-if 1-5&#xff1a;v-show&#xff08;不支…