如何通过serde_json将GCP Pub/Sub消息中base64字段直接反序列化为目标JSON类型?
问题描述
我正在使用serde_json解码GCP Pub/Sub的推送消息,当前定义了PushMessage和Message结构体,其中Message的data字段通过base64data模块将base64编码字符串解码为String。但该字符串实际是JSON序列化后的内容,我希望serde_json能直接将其处理为指定的目标类型,请问是否可行(例如通过传入泛型参数)?
相关代码如下:
#[derive(Debug, Deserialize, Serialize)] pub struct PushMessage { pub message: Message, pub subscription: String, }
#[derive(Debug, Deserialize, Serialize)] pub struct Message { message_id: String, #[serde(with = "base64data")] pub data: String, }
// A serializer/deserializer for base64-encoded data mod base64data { use serde::{Deserialize, Serialize}; use serde::{Deserializer, Serializer}; use base64::engine::general_purpose::STANDARD as base64; use base64::engine::Engine as _; pub fn serialize<S: Serializer>(v: &str, s: S) -> Result<S::Ok, S::Error> { let b64 = base64.encode(v); String::serialize(&b64, s) } pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<String, D::Error> { let b64 = String::deserialize(d)?; base64 .decode(&b64) .map(|v| String::from_utf8(v).expect("Invalid UTF-8")) .map_err(|e| serde::de::Error::custom(e)) } }
解决方案
完全可行,通过泛型参数可以让Message结构体直接将base64解码后的JSON字符串反序列化为指定目标类型,以下是具体实现步骤:
1. 重构为泛型结构体
将PushMessage和Message改为带泛型参数的结构体,泛型T代表data字段的目标类型,需约束T实现Serialize和Deserialize trait:
#[derive(Debug, Deserialize, Serialize)] pub struct PushMessage<T> { pub message: Message<T>, pub subscription: String, } #[derive(Debug, Deserialize, Serialize)] pub struct Message<T> { message_id: String, #[serde(with = "base64data")] pub data: T, }
2. 修改base64data模块支持泛型序列化/反序列化
调整模块内的序列化和反序列化逻辑,先处理base64编解码,再完成JSON与目标类型的转换:
mod base64data { use serde::{Deserialize, Serialize, Deserializer, Serializer}; use base64::engine::general_purpose::STANDARD as base64; use base64::engine::Engine as _; use serde_json; // 序列化流程:目标类型 → JSON字符串 → Base64编码 pub fn serialize<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error> where T: Serialize, S: Serializer, { let json_str = serde_json::to_string(v) .map_err(|e| serde::ser::Error::custom(e))?; let b64 = base64.encode(json_str); String::serialize(&b64, s) } // 反序列化流程:Base64字符串 → 解码为JSON字节 → UTF-8字符串 → 目标类型 pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error> where T: Deserialize<'de>, D: Deserializer<'de>, { let b64_str = String::deserialize(d)?; let decoded_bytes = base64.decode(&b64_str) .map_err(|e| serde::de::Error::custom(e))?; let json_str = String::from_utf8(decoded_bytes) .map_err(|e| serde::de::Error::custom(e))?; serde_json::from_str(&json_str) .map_err(|e| serde::de::Error::custom(e)) } }
3. 使用示例
定义你的目标数据类型,直接反序列化Pub/Sub推送消息即可:
// 示例目标类型 #[derive(Debug, Deserialize, Serialize)] pub struct MyBusinessData { pub item_id: u64, pub item_content: String, pub timestamp: u64, } fn main() -> Result<(), Box<dyn std::error::Error>> { // 模拟GCP Pub/Sub推送的JSON payload let pubsub_raw_payload = r#"{ "message": { "message_id": "msg-12345", "data": "eyJpdGVtX2lkIjoxMjMsIml0ZW1fY29udGVudCI6IlRoaXMgaXMgYSBzZWNyZXQgaXRlbSIsInRpbWVzdGFtcCI6MTY5NzA1MjgwMH0=" }, "subscription": "projects/my-gcp-project/subscriptions/my-topic-sub" }"#; // 直接反序列化为带目标类型的PushMessage let push_msg: PushMessage<MyBusinessData> = serde_json::from_str(pubsub_raw_payload)?; println!("Decoded data: {:?}", push_msg.message.data); // 输出: Decoded data: MyBusinessData { item_id: 123, item_content: "This is a secret item", timestamp: 1697052800 } Ok(()) }
核心说明
- 泛型参数
T让结构体无需重复定义即可适配任意业务数据类型 - 错误处理全程转换为serde标准错误类型,统一错误处理逻辑
- 序列化/反序列化流程完全符合GCP Pub/Sub的消息格式规范
内容的提问来源于stack exchange,提问作者beta
相关产品推荐
相关产品推荐

