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提供两种序列化与反序列化方式:
to_string与from_strto_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

