Rust常用技巧-JSON数据操作,收藏或许以后有用!
大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷《Rust常用技巧》的系列。微信公众号【梦兽编程】即可加入梦兽编程微信交流群与我交流。
语言只是我们程序员实现途径中的工具,语言的生态丰富会让我们做起事来事半功倍。从今天开始我们开始进入Rust的生态学习。这个系列会归纳到《Rust常用技巧》中,仅微信公众号。如有需要记得关注一下梦兽编程公众号。
文本含有一个计算机科普!也许在你的面试可以拿来吹吹牛逼这样子吧,
(#^.^#)。
JSON
日常开发中,JSON数据已经成为我们不可缺少的成员之一。主要的原因是JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript编程语言的一个子集,但由于其文本格式清晰,现已被广泛用于各种编程语言的数据交换。JSON数据以键值对(key/value)的形式组织,支持字符串、数字、布尔值、数组、对象(即键值对的集合)等多种数据类型。这种格式简洁、清晰,便于在不同的系统或程序之间传输数据,因此成为API接口、配置文件、数据存储等多种场景下的常用格式。
那么Rust中是如何操作Json?
Rust作为一门现代语言,标准款已经提供json模块,提供了一些基础的JSON处理功能。这个库的功能比较简单,适用于不需要复杂处理的场景。
如果你会JavaScript,那么恭喜你基础用法一模一样。但通常情况下我们不会使用标准库的json,后面会介绍另一个强大的json依赖。
如果你想解析一段JSON字符串使用起来和javascript一模一样,只不过JSON.parse换成了json::parse
let parsed = json::parse(r#"
{
"code": 200,
"success": true,
"payload": {
"features": [
"awesome",
"easyAPI",
"lowLearningCurve"
]
}
}
"#).unwrap();
序列化操作
// 比如我们需要序列化一个数组
let data = vec![1,2,3];
assert_eq!(json::stringify(data), "[1,2,3]");
对一个对象进行序列化
let mut data = json::JsonValue::new_object();
data["answer"] = 42.into();
data["foo"] = "bar".into();
assert_eq!(data.dump(), r#"{"answer":42,"foo":"bar"}"#);
但,一般我们不会这么做。因为这么写会非常啰嗦,我们能不能想JavaScript一样有一个Object的对象操作呢?答案是有的,我们可以使用object!宏进行操作。
let mut data = object!{
foo: false,
bar: null,
answer: 42,
list: [null, "world", true]
};
let json_str = json::stringify(data);
// 还是使用这个因团队而异
let json_str = data.dump();
如果你已经有一些结构体对象时候,json标准库的操作就显得非常无助。可能需要你写更多工作才能进行转换,有时候你可能还要处理JsonError 异常。
serde_json
这个依赖库主要解决了Json模块中的一点问题:
serde_json支持直接将 Rust 结构体映射到 JSON 对象,更加自然和方便。- Json 模块在解析和序列化 JSON 数据时,可能会有一定的性能开销。
serde_json通过使用过程宏和泛型编程技术,在保持易用性的同时优化了性能。serde_json提供了更加丰富和细粒度的错误处理机制,可以更好地满足复杂场景下的需求。serde_json还能处理yaml格式映射。
在处理对于处理大型JSON数据,serde_json通常表现良好,原因如下:
- 零拷贝解析:
serde_json支持零拷贝解析,它可以避免在解析过程中对数据进行不必要的复制,从而提高性能。 - 流式解析:
serde_json支持流式解析,允许你逐块处理JSON数据,而不是一次性加载整个文档到内存中。这对于处理大型JSON文件特别有用,因为它可以减少内存使用并允许更快的启动时间。 - 高效的序列化/反序列化:
serde_json生成的序列化/反序列化代码通常非常高效,因为它会为你的数据结构生成专门的代码,减少了运行时的开销。
如何使用serde-json
cargo add serde-json
use serde_json::{Result, Value};
fn untyped_example() -> Result<()> {
// 一样的例子
let data = r#"
{
"code": 200,
"success": true,
"payload": {
"features": [
"awesome",
"easyAPI",
"lowLearningCurve"
]
}
}"#;
// 将数据字符串解析为serde_json::Value。
let v: Value = serde_json::from_str(data)?;
// 我们可以这样进行访问对象的属性
// v["code"]
Ok(())
}
这是最基础的用法,我们一般管理数据肯定不会是上面这样使用serde提供的json!宏管理我们的项目数据,结构体才是软件相关的精髓。serde同样给我们提供Deserialize和Serialize 两个注解,在需要的结构体添加进去即可。
// 这是一个 嵌套的JSON
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Post {
/// 相比json模块,我们可以少些很多数据类型不同代理的问题。
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>,
}
/// 省略init数据的操作,这不是正规写法
let post = ...;
let j = serde_json::to_string(&post)?;
let post_struct = serde_json::from_slice(j).unwrap();
注意:
在大部分情况下,serde-json已经能够满足我们的需求了。但是,在一些特殊情况下,例如数据量过大,此时serde-json就有点吃力了。所以,市面上又有了一些提高 JSON 解析性能的crate。(simd-json/sonic-rs)
**sonic-rs 是有字节开源的,字节目前算是中国区对Rust贡献最大的企业。**
simd-json 大量使用 SIMD 指令集来加速 JSON 解析和序列化的性能,为了保持性能优势,simd-json 有意放弃了一些 serde_json提供的高级特性,如自动派生 Serialize/Deserialize trait。
科普:SIMD 代表"Single Instruction, Multiple Data"(单指令,多数据)。它是一种并行计算技术,可以在一个指令中同时对多个数据进行处理。SIMD 指令集通常被用于需要大量重复数值计算的场景,如图形处理、视频编解码、机器学习等。通过同时处理多个数据,SIMD 可以极大提高这些场景下的计算性能。
simd-json 是一个c++库,也有很多语言为了追求性能扩展出类似simdjson-go 、simdjson-java 等。
可以看出simdjson的强悍。
使用simd-json提供项目性能
[dependencies]
simd-json = "0.4"
解析 JSON 数据
使用 simd_json::from_slice() 函数可以快速解析 JSON 数据
use simd_json::from_slice;
let json_data = r#"{"name":"John","age":30,"email":"john@example.com"}"#;
let parsed: serde_json::Value = from_slice(json_data.as_bytes()).unwrap();
println!("{:#?}", parsed);
序列化 Rust 数据到 JSON
使用 simd_json::to_vec() 函数可以将 Rust 数据序列化为 JSON 字节数组
use simd_json::to_vec;
use serde::Serialize;
#[derive(Serialize)]
struct Person {
name: &'static str,
age: u32,
email: &'static str,
}
let person = Person {
name: "John",
age: 30,
email: "john@example.com",
};
let json_data = to_vec(&person).unwrap();
println!("{}", String::from_utf8_lossy(&json_data));
配合 Serde 使用
simd-json 可以与 Serde 生态系统无缝集成,使用 Serde 的 Serialize 和 Deserialize trait:
use serde::{Serialize, Deserialize};
use simd_json::{from_slice, to_vec};
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u32,
email: String,
}
let person = Person {
name: "John".to_string(),
age: 30,
email: "john@example.com".to_string(),
};
let json_data = to_vec(&person).unwrap();
let parsed: Person = from_slice(&json_data).unwrap();
println!("{:#?}", parsed);
虽然simd-json性能更好,但是它缺失了serde生态带来的便利性。如果是对性能有严格要求,如实时数据处理、切入式场景下选择会好点。一般的功能需求如Web服务,数据库交互等在Rust的加持下serde-json完全是够用的。
总结
以上就是rust对json数据类型操作的所有内容,大家可以按需进行选择。在符合自己的功能的场景下选择就好。一般来说serde-json 属于万金油,直接使用serde-json 即可。
如果您对Rust编程感兴趣,欢迎关注我的公众号【梦兽编程】,一起探索Rust的奥秘。如果您觉得这篇文章对您有帮助,请分享给更多需要的朋友。您的转发是我最大的动力!