You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust序列化:兼顾任意格式与可读性的字符串容器选型问询

背景信息

本问题涉及serde库以及Rust结构体序列化与反序列化的输出。

  • 目标是编写可序列化为人类可读字符串的嵌套结构。
  • 主结构的子组件需支持可变序列化方式。
  • 若子组件采用可序列化为字符串的可读格式,整个结构需保持可读。
  • 若子组件采用二进制格式序列化,仅主结构部分保持可读,子组件无需可读。

字符串

Rust的String必须(理应)是有效的UTF-8编码。(标准库的其余部分均假设这一点,因此尽管使用from_utf8_unchecked可创建非UTF-8的String,但这并非良策。)

JSON或其他格式序列化

当前我使用JSON格式序列化与反序列化Rust结构体,输出为有效的UTF-8字符串。

但JSON只是临时设计方案,未来可能切换为Protobuf或Avro等二进制格式。此时序列化输出将不再是有效的UTF-8,而是任意二进制串。

可用何种结构存储任意二进制串?最直观的选择是Vec<u8>,但存在如下问题。

另一种可能是OsString,但不确定是否为合适容器。OsString的名称不像BinaryString那样具有通用性,因此我不太清楚其设计用途,尤其是在已有Vec<u8>的情况下。

使用Serde序列化与反序列化的结果

Serde提供两种序列化与反序列化方式:

  • to_string与from_str
  • to_vec与from_slice

以下演示代码展示两种方式的输出:

use serde::Serialize;
use serde::Deserialize;

use serde_json;

#[derive(Serialize, Deserialize)]
struct MessageBody {
    pub encoded_message_body: String,
    pub encoded_message_body_vec: Vec<u8>,
}

#[derive(Serialize, Deserialize)]
struct Message {
    pub message_body: MessageBody,
}

#[derive(Serialize, Deserialize)]
struct MyType {
    pub my_data: i32,
}

fn main() {

    let my_type = MyType {
        my_data: 10,
    };
    
    // Encode as String using serde
    let encoded_my_type = serde_json::to_string(&my_type).unwrap();
    let encoded_my_type_vec = serde_json::to_vec(&my_type).unwrap();
    
    let message_body = MessageBody {
        encoded_message_body: encoded_my_type,
        encoded_message_body_vec: encoded_my_type_vec,
    };
    
    let message = Message {
        message_body: message_body,
    };
    
    //let encoded_message = serde_json::to_vec(&message).unwrap();
    let encoded_message = serde_json::to_string(&message).unwrap();
    
    println!("{:?}", encoded_message);
    
}

输出如下:

"{"message_body":{"encoded_message_body":"{\\\"my_data\\\":10}","encoded_message_body_vec":[123,34,109,121,95,100,97,116,97,34,58,49,48,125]}}"

一种输出可读,另一种不可读。encoded_message_body是可读的,encoded_message_body_vec被序列化为数值数组的字面字符串表示,尽管内存中的二进制数据是ASCII(UTF-8)格式的JSON串,但该输出不可读。

注:我选择用println!展示编码后的消息,实际数据会发送至Kafka。若包含Kafka则无法编写最小可复现示例,Kafka控制台消费者显示的内容与此完全一致。

我的预期输出更接近如下形式:

"{"message_body":{"encoded_message_body":"{\\\"my_data\\\":10}","encoded_message_body_vec":{"my_data":10}}}"
总结

我面临如下选择:

  • 使用Vec<u8>存储结果以支持任意序列化格式(JSON、Protobuf等),但输出不可读;
  • 使用String存储结果以获得可读输出,但仅支持JSON等可序列化为有效UTF-8的格式。

能否同时支持任意序列化格式与可读输出?当然,二进制格式的输出会是乱码,但JSON等格式需保持可读性优势。

进一步思考

有人(线下)指出,不能将原始二进制数据直接写入JSON格式,因为二进制数据可能包含对JSON解析器有特殊意义的字符(如{)。

这是看待问题的另一种角度,非常有价值。

  • 这意味着若外层消息采用JSON编码(我们确实如此),二进制负载必须采用Base64或Base85等方式编码。
  • 也有其他回答指出了这一点。

这让我开始思考,即使负载很大,将负载改为二进制编码可能也无益处,因为这必然需要额外的编码解码层:

[struct] <-> BinarySerializedData <-> BaseXXEncoded (String, `encoded_message_body`) 
    <-> JSON Encoded String <-> [Wire]

需通过测试了解性能影响,是否值得这么做可能取决于二进制负载的平均大小。


内容的提问来源于stack exchange,提问作者user2138149

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 16:22:14