如何用Serde正确反序列化CoinBase枚举类型消息?
解决CoinBase WebSocket消息的Serde多类型反序列化问题
问题分析
你需要反序列化两种结构差异较大的CoinBase WebSocket消息:
subscriptions类型消息包含channels字段,希望直接映射为Vec<Channel>,避免多余的包裹结构体ticker类型消息结构独立,无channels字段,需要完整映射为对应结构体
解决方案:利用Serde自定义反序列化函数
无需额外定义冗余结构体,也不用自行编写解析器,通过Serde的deserialize_with属性即可实现需求:
首先定义基础结构体:
use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Channel { name: String, product_ids: Vec<String>, } #[derive(Debug, Deserialize)] pub struct Ticker { sequence: u64, product_id: String, price: String, open_24h: String, volume_24h: String, low_24h: String, high_24h: String, volume_30d: String, best_bid: String, best_bid_size: String, best_ask: String, best_ask_size: String, side: String, time: String, trade_id: u64, last_size: String, }
然后定义消息枚举,为Subscriptions变体添加自定义反序列化逻辑:
use serde::de::Deserializer; #[derive(Debug, Deserialize)] #[serde(tag = "type")] pub enum CoinbaseMessage { #[serde(rename = "subscriptions")] #[serde(deserialize_with = "deserialize_subscriptions")] Subscriptions(Vec<Channel>), #[serde(rename = "ticker")] Ticker(Ticker), } // 自定义反序列化函数:提取channels字段并转换为Vec<Channel> fn deserialize_subscriptions<'de, D>(deserializer: D) -> Result<Vec<Channel>, D::Error> where D: Deserializer<'de>, { // 临时内部结构体,仅用于反序列化时提取channels字段 #[derive(Deserialize)] struct SubscriptionsWrapper { channels: Vec<Channel>, } let wrapper = SubscriptionsWrapper::deserialize(deserializer)?; Ok(wrapper.channels) }
方案说明
- 枚举层面通过
#[serde(tag = "type")]指定根据type字段区分消息类型 Subscriptions变体使用deserialize_with指向自定义函数,该函数内部定义临时的SubscriptionsWrapper结构体来提取channels字段,最终返回Vec<Channel>,避免了外部冗余结构体Ticker变体直接使用自动反序列化,完美适配其独立结构
是否需要自行编写解析器?
完全不需要。Serde的自定义反序列化能力已经简洁解决了问题,相比手动编写解析器,Serde能自动处理:
- 字段缺失、类型不匹配等异常情况
- 复杂嵌套结构的映射
- 代码可维护性更高,无需重复处理JSON解析的基础逻辑
内容的提问来源于stack exchange,提问作者financial_physician
相关产品推荐
相关产品推荐

