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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 09:00:47