Rust JSON 序列化完全指南

Rust 阅读约 14 分钟
RustJSON序列化serde性能优化

掌握 Rust 中使用 serde 进行 JSON 序列化的技术。学习 derive 宏、自定义序列化、零拷贝反序列化、枚举处理以及使用 serde_json 进行性能优化。

Serde:基础框架

Serde 是 Rust 的序列化框架——零成本抽象,编译时代码生成。

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    name: String,
    age: u32,
    #[serde(default)]
    email: Option<String>,
    #[serde(rename = "createdAt")]
    created_at: chrono::DateTime<chrono::Utc>,
}

// 序列化
let user = User {
    name: "张三".to_string(),
    age: 30,
    email: Some("zhangsan@example.com".to_string()),
    created_at: chrono::Utc::now(),
};
let json = serde_json::to_string_pretty(&user)?;

// 反序列化
let parsed: User = serde_json::from_str(&json)?;

字段属性

在字段级别控制序列化行为:

#[derive(Serialize, Deserialize)]
struct Config {
    // 重命名以兼容 JSON
    #[serde(rename = "databaseUrl")]
    database_url: String,

    // None 时跳过
    #[serde(skip_serializing_if = "Option::is_none")]
    optional_field: Option<String>,

    // 缺失时使用默认值
    #[serde(default = "default_timeout")]
    timeout: u64,

    // 始终跳过
    #[serde(skip)]
    internal_state: Vec<u8>,

    // 展平嵌套结构
    #[serde(flatten)]
    metadata: HashMap<String, serde_json::Value>,
}

fn default_timeout() -> u64 {
    30
}

自定义序列化

实现 SerializeDeserialize 以获得完全控制:

use serde::{Deserializer, Serializer};
use serde::de::{self, Visitor};
use std::fmt;

struct CustomDate(chrono::NaiveDate);

impl Serialize for CustomDate {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = self.0.format("%Y-%m-%d").to_string();
        serializer.serialize_str(&s)
    }
}

impl<'de> Deserialize<'de> for CustomDate {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct DateVisitor;

        impl<'de> Visitor<'de> for DateVisitor {
            type Value = CustomDate;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("YYYY-MM-DD 格式的日期字符串")
            }

            fn visit_str<E>(self, value: &str) -> Result<CustomDate, E>
            where
                E: de::Error,
            {
                chrono::NaiveDate::parse_from_str(value, "%Y-%m-%d")
                    .map(CustomDate)
                    .map_err(de::Error::custom)
            }
        }

        deserializer.deserialize_str(DateVisitor)
    }
}

枚举序列化

在 JSON 中处理 Rust 枚举:

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Shape {
    Circle { radius: f64 },
    Rectangle { width: f64, height: f64 },
    Triangle { base: f64, height: f64 },
}

// {"type": "Circle", "radius": 5.0}
let circle = Shape::Circle { radius: 5.0 };
let json = serde_json::to_string(&circle)?;

// 外部标签(默认)
#[derive(Serialize, Deserialize)]
enum Color {
    Red,
    Green,
    Blue,
}
// "Red"

// 相邻标签
#[derive(Serialize, Deserialize)]
#[serde(tag = "t", content = "c")]
enum Value {
    Int(i64),
    Text(String),
}
// {"t": "Int", "c": 42}

// 无标签枚举
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Value {
    Int(i64),
    Text(String),
    Bool(bool),
}
// 按顺序尝试:先 Int,再 Text,最后 Bool

错误处理

健壮的 JSON 解析与正确的错误类型:

use thiserror::Error;

#[derive(Error, Debug)]
enum JsonError {
    #[error("JSON 解析错误: {0}")]
    Parse(#[from] serde_json::Error),

    #[error("验证错误: {message}")]
    Validation { message: String },

    #[error("缺失字段: {field}")]
    MissingField { field: String },
}

fn parse_config(json: &str) -> Result<Config, JsonError> {
    let config: Config = serde_json::from_str(json)?;

    if config.database_url.is_empty() {
        return Err(JsonError::MissingField {
            field: "databaseUrl".to_string(),
        });
    }

    Ok(config)
}

处理动态 JSON

对于未知结构,使用 serde_json::Value:

use serde_json::{Value, json};

// 解析为动态 Value
let data: Value = serde_json::from_str(json_string)?;

// 访问字段
let name = data["name"].as_str().unwrap_or("未知");
let age = data["age"].as_i64().unwrap_or(0);

// 使用 json! 宏构建 JSON
let user = json!({
    "name": "张三",
    "age": 30,
    "tags": ["rust", "json"]
});

// 将 Value 转回字符串
let output = serde_json::to_string_pretty(&user)?;

性能优化

使用 simd-json 加速解析

use simd_json::prelude::*;

let mut data = json_string.to_owned();
let parsed: User = simd_json::from_str(&mut data)?;
// 大型载荷比 serde_json 快 2-3 倍

使用 borrow 避免分配

#[derive(Deserialize)]
struct User<'a> {
    name: &'a str,  // 从输入字符串借用
    age: u32,
}

let user: User = serde_json::from_str(&json)?;
// 字符串字段无需分配

流式处理大文件

use serde_json::Deserializer;

let reader = std::fs::File::open("large.json")?;
let stream = Deserializer::from_reader(reader).into_iter::<User>();

for result in stream {
    let user = result?;
    process_user(user);
}
// 注意:仅适用于 NDJSON(换行分隔的 JSON)格式

相关资源

各语言 JSON API 对比

语言 序列化 反序列化 美化输出 配置方式
Python json(内置) json.dumps(obj) json.loads(str) json.dumps(obj, indent=2) ensure_ascii, default, cls
JavaScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Java Jackson / Gson mapper.writeValueAsString(obj) mapper.readValue(str, Class.class) mapper.writerWithDefaultPrettyPrinter() 注解、Module、Feature
Go encoding/json(内置) json.Marshal(obj) json.Unmarshal(data, &obj) json.MarshalIndent(obj, "", " ") struct tag
C# System.Text.Json(内置) JsonSerializer.Serialize(obj) JsonSerializer.Deserialize<T>(str) WriteIndented = true JsonSerializerOptions
TypeScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Rust serde_json serde_json::to_string(&obj) serde_json::from_str::<T>(str) serde_json::to_string_pretty(&obj) serde attributes, custom (de)serializers

常见问题 (FAQ)

JSON 序列化时如何处理日期时间类型?

大多数语言的 JSON 库默认不支持日期时间类型。通常做法是序列化为 ISO 8601 格式字符串(如 "2026-07-15T10:30:00Z")或 Unix 时间戳,反序列化时再转换回日期时间对象。

如何忽略 JSON 序列化中的 null 值或空字段?

不同语言的实现方式不同:Python 可在自定义编码器中过滤 None 值;JavaScript 可使用 replacer 函数;Java Jackson 用 @JsonInclude 注解;Go 使用 omitempty struct tag;C# 设置 DefaultIgnoreCondition。

JSON 序列化时如何处理循环引用?

循环引用是 JSON 序列化的常见陷阱。标准 JSON 库通常会抛出异常或栈溢出。解决方案包括:使用 @JsonIdentityInfo 注解(Java Jackson)、实现自定义序列化器、将对象图转换为 DTO 后再序列化。

为什么我的私有字段没有被序列化?

JSON 序列化库通常只序列化公开(public)字段或带有 getter 的属性。Python 的 json 模块默认只序列化 dict 的公共键;Java Jackson 可通过 @JsonInclude 注解包含非公共成员。

JSON 序列化性能有哪些优化技巧?

1) 复用序列化器实例;2) 对于大型数据,使用流式 API;3) 使用编译时生成(C# Source Generator、Go easyjson);4) 避免过多嵌套层次;5) 使用数据库分页或分块传输大 JSON。

推荐工具

以下工具可以帮助你在 Rust 开发中更高效地处理 JSON 数据:

相关文章