serde_json

serde_json #

1cargo add serde_json

在Rust中,可能会遇到三种常见的需要处理JSON数据的方式:

  • 文本数据: 在HTTP端点接收到的、从文件中读取的、或准备发送到远程服务器的未经处理的JSON数据字符串。

  • 无类型或松散类型的表示 想在传递某些JSON数据之前验证其有效性,但不需要知道它包含的具体结构。或者想进行一些非常基础的操作,比如在特定位置插入一个键。

  • 强类型的Rust数据结构。 期望所有或大部分数据符合特定结构,并且想要在避免JSON松散特性带来麻烦的情况下完成实际工作。

Serde JSON提供了高效、灵活、安全的方法来在这些表示方式之间转换数据。

无类型或松散类型的表示 #

任何有效的JSON数据都可以在以下递归枚举表示中进行操作。这个数据结构是serde_json::Value

1enum Value {
2    Null,
3    Bool(bool),
4    Number(Number),
5    String(String),
6    Array(Vec<Value>),
7    Object(Map<String, Value>),
8}

JSON数据字符串可以通过 serde_json::from_str 函数解析成 serde_json::Value。此外还有 from_slice 用于从字节切片 &[u8] 解析,以及 from_reader 用于从任何 io::Read(比如文件或 TCP 流)解析。

 1use serde_json::{Result, Value};
 2
 3fn untyped_example() -> Result<()> {
 4    // Some JSON input data as a &str. Maybe this comes from the user.
 5    let data = r#"
 6        {
 7            "name": "John Doe",
 8            "age": 43,
 9            "phones": [
10                "+44 1234567",
11                "+44 2345678"
12            ]
13        }"#;
14
15    // Parse the string of data into serde_json::Value.
16    let v: Value = serde_json::from_str(data)?;
17
18    // Access parts of the data by indexing with square brackets.
19    println!("Please call {} at the number {}", v["name"], v["phones"][0]);
20
21    Ok(())
22}

索引操作如 v["name"] 的结果是该索引处数据的借用,因此其类型是 &Value。JSON 映射可以用字符串键进行索引,而 JSON 数组可以用整数键进行索引。如果数据类型与索引的类型不匹配,或者映射中不包含所索引的键,或者vector的索引超出范围,则返回的元素是 Value::Null

当打印 Value 时,它会被打印为 JSON 字符串。因此在上述代码中,输出看起来像 Please call "John Doe" at the number "+44 1234567"。这里出现引号是因为 v["name"] 是一个包含 JSON 字符串的 &Value,其 JSON 表示是 "John Doe"。如果要打印不带引号的纯字符串,需要使用 as_str() 将 JSON 字符串转换为 Rust 字符串,或者按照下一节所述避免使用 Value

Value 表示方式对于非常基础的任务来说是足够的,但对于更重要的任务来说可能会很繁琐。例如,要正确实现错误处理会变得很冗长,比如试图检测输入数据中是否存在未识别的字段。当你犯错时编译器也无法帮助你,例如在代码中的几十个地方中,将 v["name"] 错误地写成 v["nmae"]

将JSON解析为强类型数据结构 #

Serde提供了一种强大的方法,能够将JSON数据基本自动地映射到Rust数据结构中。

 1
 2#[derive(Serialize, Deserialize)]
 3struct Person {
 4    name: String,
 5    age: u8,
 6    phones: Vec<String>,
 7}
 8
 9fn typed_example() -> Result<()> {
10    // Some JSON input data as a &str. Maybe this comes from the user.
11    let data = r#"
12        {
13            "name": "John Doe",
14            "age": 43,
15            "phones": [
16                "+44 1234567",
17                "+44 2345678"
18            ]
19        }"#;
20
21    // Parse the string of data into a Person object. This is exactly the
22    // same function as the one that produced serde_json::Value above, but
23    // now we are asking it for a Person as output.
24    let p: Person = serde_json::from_str(data)?;
25
26    // Do things just like with any other Rust data structure.
27    println!("Please call {} at the number {}", p.name, p.phones[0]);
28
29    Ok(())
30}

Serde通过serde_json::from_str函数实现与之前相同的功能,但这次我们将返回值赋给一个Person类型的变量,这样Serde就会自动将输入数据解析为Person类型,并在数据结构与预期的Person格式不符时生成详细的错误信息。

任何实现了Serde的Deserialize特征的类型都可以通过这种方式进行反序列化。这包括Rust标准库中的内置类型,如Vec<T>HashMap<K, V>,以及任何使用#[derive(Deserialize)]标注的结构体或枚举。

一旦我们得到了Person类型的变量p,我们的IDE和Rust编译器就能像处理其他Rust代码一样帮助我们正确使用它。IDE可以自动补全字段名称以防止拼写错误,这在serde_json::Value表示方式中是不可能实现的。而且Rust编译器可以检查当我们写p.phones[0]时,p.phones一定是Vec<String>类型,所以对其进行索引是有意义的,并且会产生一个String类型的值。

构造JSON值(serde_json::Value) #

Serde JSON提供了一个json!宏,可以使用非常自然的JSON语法来构建serde_json::Value对象。

 1use serde_json::json;
 2
 3fn main() {
 4    // The type of `john` is `serde_json::Value`
 5    let john = json!({
 6        "name": "John Doe",
 7        "age": 43,
 8        "phones": [
 9            "+44 1234567",
10            "+44 2345678"
11        ]
12    });
13
14    println!("first phone number: {}", john["phones"][0]);
15
16    // Convert to a string of JSON and print it out
17    println!("{}", john.to_string());
18}

Value::to_string()函数可以将serde_json::Value转换为JSON格式的String文本。

json!宏的一个巧妙之处在于,在构建JSON值时可以直接插入变量和表达式。Serde会在编译时检查你插入的值是否可以表示为JSON。

 1let full_name = "John Doe";
 2let age_last_year = 42;
 3
 4// The type of `john` is `serde_json::Value`
 5let john = json!({
 6    "name": full_name,
 7    "age": age_last_year + 1,
 8    "phones": [
 9        format!("+44 {}", random_phone())
10    ]
11});

这种方式非常方便,但我们遇到了之前使用Value时的同样问题:如果我们出错了,IDE和Rust编译器无法帮助我们。Serde JSON提供了一种更好的方式来将强类型数据结构序列化为JSON文本。

通过序列化数据结构创建JSON #

数据结构可以通过serde_json::to_string转换为JSON字符串。此外还有serde_json::to_vec可以序列化为Vec<u8>,以及serde_json::to_writer可以序列化到任何io::Write类型,比如文件或TCP流。

 1use serde::{Deserialize, Serialize};
 2use serde_json::Result;
 3
 4#[derive(Serialize, Deserialize)]
 5struct Address {
 6    street: String,
 7    city: String,
 8}
 9
10fn print_an_address() -> Result<()> {
11    // Some data structure.
12    let address = Address {
13        street: "10 Downing Street".to_owned(),
14        city: "London".to_owned(),
15    };
16
17    // Serialize it to a JSON string.
18    let j = serde_json::to_string(&address)?;
19
20    // Print, write to a file, or send to an HTTP server.
21    println!("{}", j);
22
23    Ok(())
24}

任何实现了Serde的Serialize特征的类型都可以通过这种方式进行序列化。这包括Rust标准库中的内置类型,如Vec<T>HashMap<K, V>,以及任何使用#[derive(Serialize)]标注的结构体或枚举。

© 2024 青蛙小白