不灭的焱

革命尚未成功,同志仍须努力 下载Java21

作者:AlbertWen  添加时间:2026-07-18 17:11:03  修改时间:2026-07-23 09:39:44  分类:01.Rust编程  编辑

目录

仓库地址:https://crates.io/crates/serde_json

Rust serde_json 使用详解

serde_json 是 Rust 生态中最常用的 JSON 处理库,主要负责:

  • 将 Rust 数据转换成 JSON:序列化
  • 将 JSON 转换成 Rust 数据:反序列化
  • 使用 Value 动态读取、修改 JSON
  • 从字符串、字节数组、文件、网络流中读取 JSON
  • 将 JSON 写入字符串、字节数组、文件或网络流

严格来说:

  • serde 定义通用的序列化、反序列化框架;
  • serde_json 是 Serde 针对 JSON 格式的具体实现。 (Docs.rs)

一、添加依赖

Cargo.toml 中添加:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

也可以执行:

cargo add serde --features derive
cargo add serde_json

其中:

features = ["derive"]

用于开启:

#[derive(Serialize, Deserialize)]

自动派生宏。Serde 官方文档明确说明,SerializeDeserialize 的派生宏需要启用 derive 功能。 (Docs.rs)

二、核心概念

1. 序列化 Serialize

把 Rust 数据转换为 JSON:

Rust 结构体
    ↓
JSON 字符串

例如:

User {
    id: 1001,
    name: "张三",
}

转换成:

{
  "id": 1001,
  "name": "张三"
}

对应 Trait:

serde::Serialize

2. 反序列化 Deserialize

把 JSON 转换为 Rust 数据:

JSON 字符串
    ↓
Rust 结构体

对应 Trait:

serde::Deserialize

Serde 已经为大量 Rust 基础类型、集合类型提供了实现,也可以通过派生宏自动为结构体和枚举生成实现。 (Docs.rs)

三、最基本的结构体与 JSON 转换

1. Rust 结构体转换成 JSON

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,
    active: bool,
    roles: Vec<String>,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
        active: true,
        roles: vec![
            "admin".to_string(),
            "operator".to_string(),
        ],
    };

    let json_text = serde_json::to_string(&user)?;

    println!("{json_text}");

    Ok(())
}

输出:

{"id":1001,"username":"zhangsan","active":true,"roles":["admin","operator"]}

这里的:

serde_json::to_string(&user)

返回类型为:

Result<String, serde_json::Error>

其作用是将实现了 Serialize 的数据序列化为紧凑 JSON 字符串。 (Docs.rs)

2. 输出格式化 JSON

使用:

serde_json::to_string_pretty()

完整示例:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,
    active: bool,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
        active: true,
    };

    let json_text = serde_json::to_string_pretty(&user)?;

    println!("{json_text}");

    Ok(())
}

输出:

{
  "id": 1001,
  "username": "zhangsan",
  "active": true
}

to_string_pretty 适合:

  • 配置文件
  • 日志输出
  • 调试
  • 需要人工阅读的 JSON

to_string 更适合:

  • HTTP API 返回
  • 网络传输
  • 数据库存储
  • 减少数据体积

to_string_pretty 返回格式化后的 JSON 字符串。 (Docs.rs)

四、JSON 字符串转换为 Rust 结构体

使用:

serde_json::from_str()

示例:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
    active: bool,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "active": true
    }
    "#;

    let user: User = serde_json::from_str(json_text)?;

    println!("用户ID:{}", user.id);
    println!("用户名:{}", user.username);
    println!("是否启用:{}", user.active);

    Ok(())
}

输出:

用户ID:1001
用户名:zhangsan
是否启用:true

注意这里必须明确目标类型:

let user: User = serde_json::from_str(json_text)?;

也可以使用泛型参数:

let user = serde_json::from_str::<User>(json_text)?;

from_str 会检查 JSON 结构是否符合目标 Rust 类型。如果字段缺失、类型不匹配或数值超出目标类型范围,就会返回错误。 (Docs.rs)

五、为什么推荐使用原始字符串

Rust 普通字符串中的双引号需要转义:

let json_text = "{\"id\":1001,\"username\":\"zhangsan\"}";

这样非常难读。

建议使用 Rust 原始字符串:

let json_text = r#"
{
    "id": 1001,
    "username": "zhangsan"
}
"#;

原始字符串的基本格式是:

r#"内容"#

里面的双引号不需要写成:

\"

六、常用函数总结

函数 作用 返回类型
to_string Rust 数据转紧凑 JSON 字符串 String
to_string_pretty Rust 数据转格式化 JSON 字符串 String
to_vec Rust 数据转 JSON 字节数组 Vec<u8>
to_vec_pretty Rust 数据转格式化 JSON 字节数组 Vec<u8>
to_writer 将 JSON 写入 Write ()
to_writer_pretty 格式化后写入 Write ()
from_str 从字符串读取 JSON 指定类型
from_slice 从字节切片读取 JSON 指定类型
from_reader 从文件、网络流等读取 JSON 指定类型
to_value Rust 数据转 Value Value
from_value Value 转 Rust 数据 指定类型

这些函数构成了 serde_json 最主要的高级 API。 (Docs.rs)

七、使用 serde_json::Value 处理动态 JSON

有些 JSON 的结构并不固定,或者只需要读取其中几个字段,此时不一定要定义结构体,可以使用:

serde_json::Value

Value 可以表示任何合法 JSON 值。 (Docs.rs)

1. Value 与 JSON 类型的对应关系

serde_json::Value 大致包含以下类型:

pub enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

对应关系:

JSON 类型 Rust Value
null Value::Null
truefalse Value::Bool
数字 Value::Number
字符串 Value::String
数组 Value::Array
对象 Value::Object

2. JSON 字符串转换为 Value

use serde_json::Value;

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "code": 200,
        "message": "success",
        "data": {
            "id": 1001,
            "username": "zhangsan",
            "active": true,
            "roles": ["admin", "operator"]
        }
    }
    "#;

    let value: Value = serde_json::from_str(json_text)?;

    println!("状态码:{}", value["code"]);
    println!("用户名:{}", value["data"]["username"]);
    println!("第一个角色:{}", value["data"]["roles"][0]);

    Ok(())
}

输出:

状态码:200
用户名:"zhangsan"
第一个角色:"admin"

注意:直接打印 Value::String 时会保留 JSON 双引号。

3. 将 Value 转换成具体类型

let code = value["code"].as_u64();
let username = value["data"]["username"].as_str();
let active = value["data"]["active"].as_bool();

完整示例:

use serde_json::Value;

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "active": true
    }
    "#;

    let value: Value = serde_json::from_str(json_text)?;

    let id = value["id"]
        .as_u64()
        .ok_or_else(|| {
            serde_json::Error::io(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "id 不是 u64",
            ))
        })?;

    let username = value["username"]
        .as_str()
        .unwrap_or("未知用户");

    let active = value["active"]
        .as_bool()
        .unwrap_or(false);

    println!("id={id}");
    println!("username={username}");
    println!("active={active}");

    Ok(())
}

不过实际业务中,不建议为了构造业务错误而手动生成 serde_json::Error。更常见的做法是使用自己的错误类型、anyhowthiserror

4. 推荐使用 get 安全读取

下面这种写法很方便:

value["data"]["username"]

但当字段不存在时,索引操作通常返回:

Value::Null

这可能掩盖“字段不存在”的问题。

更稳妥的写法:

let username = value
    .get("data")
    .and_then(|data| data.get("username"))
    .and_then(Value::as_str);

完整示例:

use serde_json::Value;

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "data": {
            "username": "zhangsan"
        }
    }
    "#;

    let value: Value = serde_json::from_str(json_text)?;

    match value
        .get("data")
        .and_then(|data| data.get("username"))
        .and_then(Value::as_str)
    {
        Some(username) => println!("用户名:{username}"),
        None => println!("用户名不存在或类型不正确"),
    }

    Ok(())
}

get 在字段不存在、数组越界或类型不符合时返回 None;方括号索引则会在这些情况下返回 Value::Null。 (Docs.rs)

八、使用 json! 宏创建 JSON

serde_json 提供了非常方便的:

json!()

宏。

1. 创建 JSON 对象

use serde_json::json;

fn main() {
    let value = json!({
        "code": 200,
        "message": "success",
        "data": {
            "id": 1001,
            "username": "zhangsan",
            "active": true
        }
    });

    println!("{value}");
}

输出:

{"code":200,"data":{"active":true,"id":1001,"username":"zhangsan"},"message":"success"}

json! 的返回类型是:

serde_json::Value

官方文档说明,json! 可以用接近原生 JSON 的语法构建 Value,并支持插入 Rust 变量或表达式。 (Docs.rs)

2. 插入 Rust 变量

use serde_json::json;

fn main() {
    let user_id = 1001;
    let username = "zhangsan";
    let roles = vec!["admin", "operator"];

    let value = json!({
        "id": user_id,
        "username": username,
        "roles": roles,
        "enabled": true
    });

    println!("{value}");
}

3. 使用表达式

use serde_json::json;

fn main() {
    let page = 2;
    let page_size = 20;

    let value = json!({
        "page": page,
        "pageSize": page_size,
        "offset": (page - 1) * page_size,
        "hasNext": true
    });

    println!("{value}");
}

九、修改动态 JSON

1. 修改已有字段

use serde_json::json;

fn main() {
    let mut value = json!({
        "id": 1001,
        "username": "zhangsan",
        "active": true
    });

    value["username"] = json!("lisi");
    value["active"] = json!(false);

    println!("{value}");
}

结果:

{"active":false,"id":1001,"username":"lisi"}

2. 添加字段

use serde_json::json;

fn main() {
    let mut value = json!({
        "id": 1001
    });

    value["username"] = json!("zhangsan");
    value["roles"] = json!(["admin", "operator"]);

    println!("{value}");
}

3. 操作对象 Map

use serde_json::{json, Value};

fn main() {
    let mut value = json!({
        "id": 1001,
        "username": "zhangsan"
    });

    if let Value::Object(object) = &mut value {
        object.insert("active".to_string(), json!(true));
        object.remove("username");
    }

    println!("{value}");
}

4. 操作数组

use serde_json::{json, Value};

fn main() {
    let mut value = json!({
        "roles": ["admin", "operator"]
    });

    if let Some(Value::Array(roles)) = value.get_mut("roles") {
        roles.push(json!("auditor"));
    }

    println!("{value}");
}

输出:

{"roles":["admin","operator","auditor"]}

十、Serde 字段属性详解

Serde 提供了大量属性,用于控制字段名称、默认值、忽略规则等。 (Serde)

1. rename:修改 JSON 字段名称

Rust 通常使用蛇形命名:

user_name

而 JSON API 可能使用:

userName

可以这样处理:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,

    #[serde(rename = "userName")]
    user_name: String,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        user_name: "zhangsan".to_string(),
    };

    println!("{}", serde_json::to_string_pretty(&user)?);

    Ok(())
}

输出:

{
  "id": 1001,
  "userName": "zhangsan"
}

2. rename_all:统一修改所有字段

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct UserProfile {
    user_id: u64,
    user_name: String,
    login_count: u32,
}

fn main() -> Result<(), serde_json::Error> {
    let profile = UserProfile {
        user_id: 1001,
        user_name: "zhangsan".to_string(),
        login_count: 5,
    };

    println!("{}", serde_json::to_string_pretty(&profile)?);

    Ok(())
}

输出:

{
  "userId": 1001,
  "userName": "zhangsan",
  "loginCount": 5
}

常用命名规则:

#[serde(rename_all = "camelCase")]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "PascalCase")]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[serde(rename_all = "kebab-case")]

3. alias:兼容旧字段名称

假设旧接口返回:

{
  "nickname": "zhangsan"
}

新接口返回:

{
  "displayName": "zhangsan"
}

可以同时兼容:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    #[serde(rename = "displayName", alias = "nickname")]
    display_name: String,
}

fn main() -> Result<(), serde_json::Error> {
    let old_json = r#"{"nickname":"zhangsan"}"#;
    let new_json = r#"{"displayName":"lisi"}"#;

    let old_user: User = serde_json::from_str(old_json)?;
    let new_user: User = serde_json::from_str(new_json)?;

    println!("{old_user:?}");
    println!("{new_user:?}");

    Ok(())
}

4. default:字段缺失时使用默认值

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    host: String,

    #[serde(default)]
    debug: bool,

    #[serde(default)]
    port: u16,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "host": "127.0.0.1"
    }
    "#;

    let config: Config = serde_json::from_str(json_text)?;

    println!("{config:#?}");

    Ok(())
}

结果:

Config {
    host: "127.0.0.1",
    debug: false,
    port: 0,
}

因为:

bool::default() == false
u16::default() == 0
String::default() == ""
Vec::default() == []
Option::default() == None

5. 自定义默认值

use serde::Deserialize;

fn default_host() -> String {
    "127.0.0.1".to_string()
}

fn default_port() -> u16 {
    8080
}

#[derive(Debug, Deserialize)]
struct Config {
    #[serde(default = "default_host")]
    host: String,

    #[serde(default = "default_port")]
    port: u16,
}

fn main() -> Result<(), serde_json::Error> {
    let config: Config = serde_json::from_str("{}")?;

    println!("{config:#?}");

    Ok(())
}

输出:

Config {
    host: "127.0.0.1",
    port: 8080,
}

Serde 支持通过 #[serde(default)] 使用 Default::default(),也支持通过 #[serde(default = "函数名")] 调用指定函数生成默认值。 (Serde)

6. skip_serializing:序列化时忽略字段

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,

    #[serde(skip_serializing)]
    password: String,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
        password: "123456".to_string(),
    };

    println!("{}", serde_json::to_string_pretty(&user)?);

    Ok(())
}

输出:

{
  "id": 1001,
  "username": "zhangsan"
}

但是要注意:

#[serde(skip_serializing)]

只在序列化时忽略,反序列化时仍然会尝试读取该字段。官方建议,如果序列化和反序列化都需要忽略,应使用 skip。 (Serde)

7. skip:序列化和反序列化都忽略

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,

    #[serde(skip)]
    internal_token: String,
}

反序列化时,internal_token 会使用其默认值:

String::default()

也就是空字符串。

8. skip_serializing_if:满足条件时忽略

这是处理 Option 最常见的写法:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<String>,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
        email: None,
    };

    println!("{}", serde_json::to_string_pretty(&user)?);

    Ok(())
}

输出:

{
  "id": 1001,
  "username": "zhangsan"
}

如果不加这个属性,默认输出:

{
  "id": 1001,
  "username": "zhangsan",
  "email": null
}

十一、Option<T> 处理可选字段

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    email: Option<String>,
}

fn main() -> Result<(), serde_json::Error> {
    let json1 = r#"
    {
        "id": 1001,
        "email": "user@example.com"
    }
    "#;

    let json2 = r#"
    {
        "id": 1002,
        "email": null
    }
    "#;

    let json3 = r#"
    {
        "id": 1003
    }
    "#;

    let user1: User = serde_json::from_str(json1)?;
    let user2: User = serde_json::from_str(json2)?;
    let user3: User = serde_json::from_str(json3)?;

    println!("{user1:?}");
    println!("{user2:?}");
    println!("{user3:?}");

    Ok(())
}

结果:

User { id: 1001, email: Some("user@example.com") }
User { id: 1002, email: None }
User { id: 1003, email: None }

对于普通的 Option<T>

  • 字段存在且有值:Some(value)
  • 字段值为 nullNone
  • 字段不存在:通常也是 None

十二、嵌套结构体

实际 API 中经常存在嵌套 JSON。

JSON:

{
  "id": 1001,
  "username": "zhangsan",
  "department": {
    "id": 10,
    "name": "IT运维部"
  }
}

Rust:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Department {
    id: u64,
    name: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,
    department: Department,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "department": {
            "id": 10,
            "name": "IT运维部"
        }
    }
    "#;

    let user: User = serde_json::from_str(json_text)?;

    println!("用户:{}", user.username);
    println!("部门:{}", user.department.name);

    Ok(())
}

十三、JSON 数组

1. 数组转换为 Vec<T>

JSON:

[
  {
    "id": 1001,
    "username": "zhangsan"
  },
  {
    "id": 1002,
    "username": "lisi"
  }
]

Rust:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    [
        {
            "id": 1001,
            "username": "zhangsan"
        },
        {
            "id": 1002,
            "username": "lisi"
        }
    ]
    "#;

    let users: Vec<User> = serde_json::from_str(json_text)?;

    for user in users {
        println!("{}:{}", user.id, user.username);
    }

    Ok(())
}

2. 结构体中包含数组

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
    roles: Vec<String>,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "roles": ["admin", "operator"]
    }
    "#;

    let user: User = serde_json::from_str(json_text)?;

    for role in user.roles {
        println!("角色:{role}");
    }

    Ok(())
}

十四、处理 HashMap

JSON 对象可以反序列化为:

HashMap<String, T>

示例:

use std::collections::HashMap;

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "host": "127.0.0.1",
        "port": "8080",
        "environment": "production"
    }
    "#;

    let config: HashMap<String, String> =
        serde_json::from_str(json_text)?;

    println!("host={:?}", config.get("host"));
    println!("port={:?}", config.get("port"));

    Ok(())
}

也可以序列化:

use std::collections::HashMap;

fn main() -> Result<(), serde_json::Error> {
    let mut map = HashMap::new();

    map.insert("host", "127.0.0.1");
    map.insert("port", "8080");

    let json_text = serde_json::to_string_pretty(&map)?;

    println!("{json_text}");

    Ok(())
}

JSON 对象的键本质上必须是字符串。对于复杂的非字符串 Map 键,序列化可能失败。serde_json 的序列化函数文档也特别列出了非字符串 Map 键这一错误场景。 (Docs.rs)

十五、使用 flatten 展开字段

1. 展开嵌套结构体

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Pagination {
    page: u32,
    page_size: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    code: u16,
    message: String,

    #[serde(flatten)]
    pagination: Pagination,
}

fn main() -> Result<(), serde_json::Error> {
    let response = ApiResponse {
        code: 200,
        message: "success".to_string(),
        pagination: Pagination {
            page: 1,
            page_size: 20,
        },
    };

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

输出:

{
  "code": 200,
  "message": "success",
  "page": 1,
  "page_size": 20
}

没有 flatten 时会变成:

{
  "code": 200,
  "message": "success",
  "pagination": {
    "page": 1,
    "page_size": 20
  }
}

2. 捕获未知字段

use std::collections::HashMap;

use serde::Deserialize;
use serde_json::Value;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,

    #[serde(flatten)]
    extra: HashMap<String, Value>,
}

fn main() -> Result<(), serde_json::Error> {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "department": "IT",
        "loginCount": 20,
        "enabled": true
    }
    "#;

    let user: User = serde_json::from_str(json_text)?;

    println!("id={}", user.id);
    println!("username={}", user.username);
    println!("其他字段={:#?}", user.extra);

    Ok(())
}

flatten 可以将结构体或 Map 的字段内联到父级,也可以用 Map 收集剩余字段。但它不能和同一结构中的 deny_unknown_fields 一起使用。 (Serde)

十六、拒绝未知字段

默认情况下,多余的 JSON 字段通常会被忽略:

{
  "id": 1001,
  "username": "zhangsan",
  "unknownField": "abc"
}

如果希望出现未知字段时直接报错,可以使用:

#[serde(deny_unknown_fields)]

示例:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct User {
    id: u64,
    username: String,
}

fn main() {
    let json_text = r#"
    {
        "id": 1001,
        "username": "zhangsan",
        "unknownField": "abc"
    }
    "#;

    match serde_json::from_str::<User>(json_text) {
        Ok(user) => println!("{user:?}"),
        Err(error) => println!("解析失败:{error}"),
    }
}

这适合:

  • 严格配置文件
  • 内部协议
  • 对数据结构要求严格的系统
  • 防止前端或第三方传入拼写错误字段

deny_unknown_fields 会在反序列化遇到未知字段时返回错误。 (Serde)

十七、枚举与 JSON

1. 默认的外部标签形式

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
enum UserStatus {
    Enabled,
    Disabled,
    Locked,
}

fn main() -> Result<(), serde_json::Error> {
    let status = UserStatus::Enabled;

    println!("{}", serde_json::to_string(&status)?);

    Ok(())
}

输出:

"Enabled"

带数据的枚举:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
enum Event {
    UserCreated {
        user_id: u64,
        username: String,
    },
    UserDeleted {
        user_id: u64,
    },
}

fn main() -> Result<(), serde_json::Error> {
    let event = Event::UserCreated {
        user_id: 1001,
        username: "zhangsan".to_string(),
    };

    println!("{}", serde_json::to_string_pretty(&event)?);

    Ok(())
}

默认输出:

{
  "UserCreated": {
    "user_id": 1001,
    "username": "zhangsan"
  }
}

这叫外部标签:

externally tagged

2. 内部标签形式

API 中更常见的是:

{
  "type": "user_created",
  "userId": 1001,
  "username": "zhangsan"
}

可以这样定义:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(
    tag = "type",
    rename_all = "snake_case",
    rename_all_fields = "camelCase"
)]
enum Event {
    UserCreated {
        user_id: u64,
        username: String,
    },
    UserDeleted {
        user_id: u64,
    },
    Heartbeat,
}

fn main() -> Result<(), serde_json::Error> {
    let event = Event::UserCreated {
        user_id: 1001,
        username: "zhangsan".to_string(),
    };

    println!("{}", serde_json::to_string_pretty(&event)?);

    Ok(())
}

输出类似:

{
  "type": "user_created",
  "userId": 1001,
  "username": "zhangsan"
}

内部标签通过:

#[serde(tag = "type")]

实现。

3. 相邻标签形式

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
enum Event {
    UserCreated {
        user_id: u64,
    },
    Message(String),
}

fn main() -> Result<(), serde_json::Error> {
    let event = Event::Message("系统维护".to_string());

    println!("{}", serde_json::to_string_pretty(&event)?);

    Ok(())
}

输出:

{
  "type": "Message",
  "data": "系统维护"
}

4. untagged:无标签枚举

有些接口中的 ID 可能是数字,也可能是字符串:

1001

或者:

"1001"

可以使用:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum UserId {
    Number(u64),
    Text(String),
}

fn main() -> Result<(), serde_json::Error> {
    let id1: UserId = serde_json::from_str("1001")?;
    let id2: UserId = serde_json::from_str(r#""U1001""#)?;

    println!("{id1:?}");
    println!("{id2:?}");

    Ok(())
}

结果:

Number(1001)
Text("U1001")

对于 untagged,Serde 会按照枚举变体声明顺序尝试反序列化,首个成功匹配的变体会被采用。因此,多个结构相似的变体需要注意排列顺序。Serde 一共支持外部标签、内部标签、相邻标签和无标签四种主要枚举表示方式。 (Serde)

十八、Rust 数据与 Value 相互转换

1. 结构体转换为 Value

use serde::Serialize;
use serde_json::Value;

#[derive(Debug, Serialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
    };

    let value: Value = serde_json::to_value(&user)?;

    println!("{value}");

    Ok(())
}

2. Value 转换为结构体

use serde::Deserialize;
use serde_json::json;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let value = json!({
        "id": 1001,
        "username": "zhangsan"
    });

    let user: User = serde_json::from_value(value)?;

    println!("{user:?}");

    Ok(())
}

注意:

from_value(value)

会取得 value 的所有权。

如果后面还要继续使用原来的 Value,需要:

let user: User = serde_json::from_value(value.clone())?;

to_valuefrom_value 专门用于 Rust 类型和动态 Value 之间的转换。 (Docs.rs)

十九、读取 JSON 文件

假设有文件:

config.json

内容:

{
  "host": "127.0.0.1",
  "port": 8080,
  "debug": true
}

代码:

use std::fs::File;
use std::io::BufReader;

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    host: String,
    port: u16,
    debug: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = File::open("config.json")?;
    let reader = BufReader::new(file);

    let config: Config = serde_json::from_reader(reader)?;

    println!("{config:#?}");

    Ok(())
}

处理文件时推荐:

BufReader<File>

因为 serde_json::from_reader 本身不会自动为输入增加缓冲。官方文档也建议对 File 等数据源自行使用缓冲。 (Docs.rs)

二十、将 JSON 写入文件

use std::fs::File;
use std::io::BufWriter;

use serde::Serialize;

#[derive(Debug, Serialize)]
struct Config {
    host: String,
    port: u16,
    debug: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config {
        host: "127.0.0.1".to_string(),
        port: 8080,
        debug: true,
    };

    let file = File::create("config.json")?;
    let writer = BufWriter::new(file);

    serde_json::to_writer_pretty(writer, &config)?;

    Ok(())
}

写入后的文件:

{
  "host": "127.0.0.1",
  "port": 8080,
  "debug": true
}

to_writer 写入紧凑 JSON,to_writer_pretty 写入格式化 JSON。两者都接受实现了 std::io::Write 的目标。 (Docs.rs)

二十一、处理字节数组

网络请求、消息队列、文件读取中,JSON 经常是:

&[u8]

1. 字节数组转结构体

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let bytes = br#"
    {
        "id": 1001,
        "username": "zhangsan"
    }
    "#;

    let user: User = serde_json::from_slice(bytes)?;

    println!("{user:?}");

    Ok(())
}

2. 结构体转字节数组

use serde::Serialize;

#[derive(Debug, Serialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let user = User {
        id: 1001,
        username: "zhangsan".to_string(),
    };

    let bytes: Vec<u8> = serde_json::to_vec(&user)?;

    println!("{bytes:?}");

    let text = String::from_utf8_lossy(&bytes);
    println!("{text}");

    Ok(())
}

from_slice 从 JSON 字节切片反序列化,to_vec 则序列化为 UTF-8 JSON 字节数组。 (Docs.rs)

二十二、通用 API 响应结构

实际 Web 项目中,经常会定义:

{
  "code": 200,
  "message": "success",
  "data": {
    "id": 1001,
    "username": "zhangsan"
  }
}

可以使用泛型:

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse<T> {
    code: u16,
    message: String,
    data: T,
}

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,
}

fn main() -> Result<(), serde_json::Error> {
    let response = ApiResponse {
        code: 200,
        message: "success".to_string(),
        data: User {
            id: 1001,
            username: "zhangsan".to_string(),
        },
    };

    let json_text = serde_json::to_string_pretty(&response)?;

    println!("{json_text}");

    let parsed: ApiResponse<User> =
        serde_json::from_str(&json_text)?;

    println!("{parsed:#?}");

    Ok(())
}

列表接口:

type UserListResponse = ApiResponse<Vec<User>>;

例如:

let response: ApiResponse<Vec<User>> =
    serde_json::from_str(json_text)?;

二十三、固定字段加动态字段

有时候 API 的基础结构固定,但 data 不固定:

use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Serialize, Deserialize)]
struct ApiResponse {
    code: u16,
    message: String,
    data: Value,
}

使用:

use serde_json::json;

fn main() -> Result<(), serde_json::Error> {
    let response = ApiResponse {
        code: 200,
        message: "success".to_string(),
        data: json!({
            "id": 1001,
            "username": "zhangsan"
        }),
    };

    println!("{}", serde_json::to_string_pretty(&response)?);

    Ok(())
}

这种方式适合:

  • 网关
  • 日志系统
  • 消息转发
  • 数据结构尚未确定
  • 接口响应类型很多

但是普通业务代码中,能够定义具体结构体时,应优先使用具体结构体,因为它有:

  • 编译期类型检查
  • 更清晰的字段含义
  • 更好的 IDE 提示
  • 更容易重构
  • 更少的运行时错误

二十四、错误处理

不推荐在正式业务代码中到处使用:

unwrap()

例如:

let user: User = serde_json::from_str(json_text).unwrap();

JSON 一旦格式错误,程序就会 panic。

推荐使用:

?

或者显式处理错误。

1. 使用 match

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: u64,
    username: String,
}

fn main() {
    let json_text = r#"
    {
        "id": "不是数字",
        "username": "zhangsan"
    }
    "#;

    match serde_json::from_str::<User>(json_text) {
        Ok(user) => {
            println!("解析成功:{user:?}");
        }
        Err(error) => {
            println!("解析失败:{error}");
            println!("错误行:{}", error.line());
            println!("错误列:{}", error.column());
            println!("错误类别:{:?}", error.classify());
        }
    }
}

2. 错误分类

serde_json::Error 可以分为:

Category::Io
Category::Syntax
Category::Data
Category::Eof

含义:

类型 含义
Io 文件、网络流等读写失败
Syntax JSON 语法错误
Data JSON 合法,但与 Rust 类型不匹配
Eof JSON 数据意外结束

例如:

use serde_json::error::Category;

fn print_json_error(error: &serde_json::Error) {
    match error.classify() {
        Category::Io => {
            println!("JSON 输入输出错误");
        }
        Category::Syntax => {
            println!(
                "JSON 语法错误,位置:{}:{}",
                error.line(),
                error.column()
            );
        }
        Category::Data => {
            println!(
                "JSON 数据类型不匹配,位置:{}:{}",
                error.line(),
                error.column()
            );
        }
        Category::Eof => {
            println!("JSON 内容不完整");
        }
    }

    println!("详细错误:{error}");
}

serde_json::Error 官方提供了 linecolumnclassifyis_syntaxis_datais_iois_eof 等方法。 (Docs.rs)

二十五、语法错误和数据错误的区别

1. Syntax:JSON 本身不合法

{
  "id": 1001,
  "username": "zhangsan",
}

最后一个字段后面多了逗号。

或者:

{
  'id': 1001
}

JSON 字段名使用了单引号。

这些属于 JSON 语法错误。

2. Data:JSON 合法,但类型不匹配

结构体:

struct User {
    id: u64,
}

JSON:

{
  "id": "1001"
}

JSON 本身合法,但 "1001" 是字符串,而结构体要求 u64,因此属于数据类型错误。

二十六、处理字段可能是字符串或数字

第三方接口可能返回:

{
  "id": 1001
}

也可能返回:

{
  "id": "1001"
}

可以使用无标签枚举:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum NumberOrString {
    Number(u64),
    String(String),
}

#[derive(Debug, Deserialize)]
struct User {
    id: NumberOrString,
}

impl NumberOrString {
    fn into_u64(self) -> Result<u64, String> {
        match self {
            Self::Number(value) => Ok(value),
            Self::String(value) => value
                .parse::<u64>()
                .map_err(|error| format!("无效数字:{error}")),
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let json1 = r#"{"id":1001}"#;
    let json2 = r#"{"id":"1002"}"#;

    let user1: User = serde_json::from_str(json1)?;
    let user2: User = serde_json::from_str(json2)?;

    println!("id1={}", user1.id.into_u64()?);
    println!("id2={}", user2.id.into_u64()?);

    Ok(())
}

二十七、连续读取多个 JSON 对象

对于日志或 JSON 流:

{"id":1,"message":"start"}
{"id":2,"message":"running"}
{"id":3,"message":"finished"}

可以使用:

serde_json::Deserializer

示例:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct LogEntry {
    id: u64,
    message: String,
}

fn main() {
    let input = r#"
        {"id":1,"message":"start"}
        {"id":2,"message":"running"}
        {"id":3,"message":"finished"}
    "#;

    let stream =
        serde_json::Deserializer::from_str(input)
            .into_iter::<LogEntry>();

    for item in stream {
        match item {
            Ok(log) => println!("{log:?}"),
            Err(error) => {
                eprintln!("解析失败:{error}");
                break;
            }
        }
    }
}

serde_json::StreamDeserializer 可以将输入流中的多个连续 JSON 值作为迭代器逐个反序列化。 (Docs.rs)

二十八、一个较完整的配置文件案例

config.json

{
  "server": {
    "host": "0.0.0.0",
    "port": 8080
  },
  "database": {
    "url": "mysql://root:password@127.0.0.1/app",
    "maxConnections": 20
  },
  "features": {
    "enableAudit": true,
    "enableRegistration": false
  },
  "logLevel": "info"
}

Rust:

use std::fs::File;
use std::io::BufReader;

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct AppConfig {
    server: ServerConfig,
    database: DatabaseConfig,
    features: FeatureConfig,

    #[serde(default = "default_log_level")]
    log_level: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
    host: String,
    port: u16,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DatabaseConfig {
    url: String,
    max_connections: u32,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeatureConfig {
    #[serde(default)]
    enable_audit: bool,

    #[serde(default)]
    enable_registration: bool,
}

fn default_log_level() -> String {
    "info".to_string()
}

fn load_config(
    path: &str,
) -> Result<AppConfig, Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);

    let config = serde_json::from_reader(reader)?;

    Ok(config)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = load_config("config.json")?;

    println!("监听地址:{}", config.server.host);
    println!("监听端口:{}", config.server.port);
    println!("数据库:{}", config.database.url);
    println!(
        "最大连接数:{}",
        config.database.max_connections
    );
    println!("审计功能:{}", config.features.enable_audit);
    println!("日志级别:{}", config.log_level);

    Ok(())
}

二十九、结构体还是 Value,应该怎么选

优先使用结构体的场景

#[derive(Serialize, Deserialize)]
struct User {
    id: u64,
    username: String,
}

适合:

  • JSON 结构明确
  • 内部业务系统
  • API 请求参数
  • API 响应对象
  • 配置文件
  • 数据库扩展 JSON 字段有固定结构

优点:

  • 类型安全
  • IDE 自动提示
  • 编译时发现部分问题
  • 字段含义明确
  • 容易维护

使用 Value 的场景

let value: serde_json::Value =
    serde_json::from_str(json_text)?;

适合:

  • JSON 结构完全动态
  • 只读取少量字段
  • API 网关透传
  • 消息转发
  • 日志采集
  • 接收未知第三方数据
  • 某些字段结构不固定

混合使用

#[derive(Deserialize)]
struct ApiRequest {
    request_id: String,
    action: String,
    data: serde_json::Value,
}

这是实际项目中很常见的折中方案:

  • 外层协议结构固定;
  • data 根据 action 动态变化。

三十、常见问题与踩坑点

1. JSON 必须使用双引号

错误:

{
  'name': 'zhangsan'
}

正确:

{
  "name": "zhangsan"
}

2. JSON 不允许尾随逗号

错误:

{
  "id": 1001,
  "name": "zhangsan",
}

正确:

{
  "id": 1001,
  "name": "zhangsan"
}

3. 必填字段缺失会报错

结构体:

struct User {
    id: u64,
    username: String,
}

JSON:

{
  "id": 1001
}

会报类似:

missing field `username`

解决方式:

#[serde(default)]
username: String

或者:

username: Option<String>

4. null 不能直接转换为普通类型

JSON:

{
  "email": null
}

错误定义:

email: String

推荐:

email: Option<String>

5. 字符串数字不能自动变成数字

JSON:

{
  "id": "1001"
}

结构体:

id: u64

默认不会自动转换。

需要:

  • 修改结构体为 String
  • 使用 #[serde(untagged)]
  • 或实现自定义反序列化。

6. 不要过度依赖方括号索引

value["data"]["user"]["name"]

字段缺失时可能得到 Value::Null,而不是马上报错。

重要业务字段推荐使用:

value
    .get("data")
    .and_then(|value| value.get("user"))
    .and_then(|value| value.get("name"))
    .and_then(Value::as_str)

或者直接定义结构体。

7. 不要在业务代码中大量使用 unwrap

不推荐:

let user: User = serde_json::from_str(json).unwrap();

推荐:

let user: User = serde_json::from_str(json)?;

或者:

match serde_json::from_str::<User>(json) {
    Ok(user) => println!("{user:?}"),
    Err(error) => eprintln!("JSON 解析失败:{error}"),
}

8. JSON 对象字段顺序不应作为业务逻辑

JSON 对象在业务语义上应被视为键值集合,不应依赖字段输出顺序。

serde_json::Value::Object 默认使用排序 Map;开启:

serde_json = {
    version = "1.0",
    features = ["preserve_order"]
}

后可使用保持插入顺序的实现。 (Docs.rs)

三十一、推荐的项目使用方式

对于正常的 Rust Web 项目,建议按下面的方式使用。

请求参数

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateUserRequest {
    username: String,
    email: Option<String>,
    role_ids: Vec<u64>,
}

响应数据

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct UserResponse {
    id: u64,
    username: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<String>,
}

通用响应结构

#[derive(Debug, Serialize)]
struct ApiResponse<T> {
    code: u16,
    message: String,
    data: T,
}

动态扩展字段

use std::collections::HashMap;

use serde_json::Value;

#[derive(Debug, Serialize, Deserialize)]
struct Asset {
    id: u64,
    name: String,

    #[serde(default)]
    attributes: HashMap<String, Value>,
}

三十二、最终记忆表

最常用的几个操作可以记成:

// Rust 数据 -> JSON 字符串
let json = serde_json::to_string(&value)?;

// Rust 数据 -> 格式化 JSON 字符串
let json = serde_json::to_string_pretty(&value)?;

// JSON 字符串 -> Rust 结构体
let value: User = serde_json::from_str(json_text)?;

// JSON 字节 -> Rust 结构体
let value: User = serde_json::from_slice(bytes)?;

// 文件、网络流 -> Rust 结构体
let value: User = serde_json::from_reader(reader)?;

// 创建动态 JSON
let value = serde_json::json!({
    "code": 200,
    "message": "success"
});

// Rust 数据 -> 动态 Value
let value = serde_json::to_value(&user)?;

// 动态 Value -> Rust 数据
let user: User = serde_json::from_value(value)?;

核心选择原则是:

JSON 结构固定
    → 使用 struct + Serialize/Deserialize

JSON 结构不固定
    → 使用 serde_json::Value

外层固定、内部动态
    → struct + Value

需要容错字段
    → Option、default、alias

需要严格校验
    → deny_unknown_fields