如何用Serde JSON Visitor将嵌套JSON反序列化为泛型Rust结构体
泛型Serde反序列化实现问题
问题背景
给定嵌套JSON结构如下:
{ "params": { "foo": { "type": "integer" }, "bar": { "type": "integer", "choices": [ 0, 26, 4 ] }, "foobar": { "type": "decimal" }, "foobaz": { "type": "decimal", "choices": [ 0.1, 26.6, 4 ] } } }
该结构中,params下的每个对象包含必填的type字段(取值为integer或decimal),以及可选的choices数组。
期望反序列化目标
希望将上述JSON反序列化为如下Rust泛型结构体:
#[derive(Debug)] pub struct OrderParameter<S> { choices: Option<Vec<S>>, }
其中S为rust_decimal::Decimal或isize类型,最终期望得到的结果(伪代码):
foo: OrderParameter<isize> {choices: None} bar: OrderParameter<isize> {choices: Some(Vec(0_i, 26_i,...)} foobar: OrderParameter<Decimal> {choices: None} foobaz: OrderParameter<Decimal> {choices: Some(Vec(Decimal(0,1)...)}
现有代码
目前的实现代码如下:
use super::errors::ParseError; use rust_decimal::prelude::*; use serde::{de, de::MapAccess, de::SeqAccess, de::Visitor, Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; use std::str::FromStr; #[derive(PartialEq)] pub enum OrderParameterType { Integer, Decimal, } impl FromStr for OrderParameterType { type Err = ParseError; fn from_str(v: &str) -> Result<Self, Self::Err> { match v { "integer" => Ok(Self::Integer), "decimal" => Ok(Self::Decimal), _ => Err(ParseError), } } } #[derive(Debug)] pub struct OrderParameter<S> { choices: Option<Vec<S>>, } impl<'de, S> Deserialize<'de> for OrderParameter<S> { // Compiler suggests: consider restricting type parameter `S`: `: parser::types::participant::_::_serde::Deserialize<'_>`, but adding S: Deserialize<'de> to the where block results in: impl has stricter requirements than trait impl has extra requirement `S: Deserialize<'de>` fn deserialize<D>(deserializer: D) -> Result<OrderParameter<S>, D::Error> where D: Deserializer<'de>, { enum Field { Variant, Choices, } impl<'de> Deserialize<'de> for Field { fn deserialize<D>(deserializer: D) -> Result<Field, D::Error> where D: Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`type` or `choices`") } fn visit_str<E>(self, value: &str) -> Result<Field, E> where E: de::Error, { match value { "type" => Ok(Field::Variant), "choices" => Ok(Field::Choices), _ => Err(de::Error::unknown_field(value, FIELDS)), } } } deserializer.deserialize_identifier(FieldVisitor) } } struct OrderParameterVisitor<T> { marker: PhantomData<fn() -> OrderParameter<T>>, } impl<T> OrderParameterVisitor<T> { fn new() -> Self { Self { marker: PhantomData, } } } impl<'de, T> Visitor<'de> for OrderParameterVisitor<T> where T: Deserialize<'de>, { type Value = OrderParameter<T>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct OrderParameter") } fn visit_map<V>(self, mut map: V) -> Result<OrderParameter<T>, V::Error> where V: MapAccess<'de>, { let mut variant = None; let mut choices: Option<Vec<T>>; while let Some(key) = map.next_key()? { match key { Field::Variant => { if variant.is_some() { return Err(de::Error::duplicate_field("type")); } variant = Some(OrderParameterType::from_str(map.next_value()?).unwrap()); } Field::Choices => { if choices.is_some() { return Err(de::Error::duplicate_field("choices")); } choices = Some(map.next_value()?); } } } let variant = variant.ok_or_else(|| de::Error::missing_field("type"))?; if choices.is_some() { if variant == OrderParameterType::Decimal { let mapped_choices: Vec<Decimal>; mapped_choices = Some(choices) .iter() .map(|x| Decimal::from_str(x).unwrap()) .collect::<Vec<Decimal>>() } else if variant == OrderParameterType::Integer { let mapped_choices: Vec<isize> = Vec::from([0, 1]); } else if variant == OrderParameterType::Wallet { choices = None } else if variant == OrderParameterType::Participant { choices = None } } Ok(OrderParameter { choices }) } } const FIELDS: &'static [&'static str] = &["type", "choices"]; deserializer.deserialize_struct("OrderParameter", FIELDS, OrderParameterVisitor::new()) } }
遇到的问题
- 编译器提示需为
S添加Deserialize<'_>约束,但添加后会出现“impl要求比trait严格”的错误。 choices字段的处理逻辑混乱:未初始化就调用is_some(),且试图在泛型实现中强制转换类型,无法根据type字段动态匹配S的实际类型。
解决方案
核心思路
由于OrderParameter<S>的泛型参数S由JSON中的type字段决定(运行时动态确定),无法直接为OrderParameter<S>实现Deserialize trait(Serde要求泛型参数的约束必须在编译时确定)。因此需要通过中间结构体先解析原始字段,再根据type分支转换为对应泛型实例的方式实现。
具体实现步骤
1. 定义中间结构体解析原始JSON字段
先定义一个RawOrderParameter结构体,用于直接解析JSON中的type和原始choices值:
#[derive(Deserialize)] struct RawOrderParameter { #[serde(rename = "type")] param_type: OrderParameterType, choices: Option<serde_json::Value>, }
2. 为不同泛型的OrderParameter实现转换逻辑
针对isize和Decimal类型,分别实现从RawOrderParameter的转换:
impl OrderParameter<isize> { fn from_raw(raw: RawOrderParameter) -> Result<Self, ParseError> { // 校验类型匹配 if raw.param_type != OrderParameterType::Integer { return Err(ParseError); } // 转换choices数组 let choices = raw.choices.map(|v| { serde_json::from_value(v) .map_err(|_| ParseError) }).transpose()?; Ok(Self { choices }) } } impl OrderParameter<Decimal> { fn from_raw(raw: RawOrderParameter) -> Result<Self, ParseError> { if raw.param_type != OrderParameterType::Decimal { return Err(ParseError); } let choices = raw.choices.map(|v| { serde_json::from_value(v) .map_err(|_| ParseError) }).transpose()?; Ok(Self { choices }) } }
3. 定义枚举包裹不同类型的OrderParameter
由于Rust中HashMap无法存储不同泛型参数的OrderParameter,需要用枚举统一包装:
#[derive(Debug)] pub enum OrderParameterEnum { Integer(OrderParameter<isize>), Decimal(OrderParameter<Decimal>), }
4. 顶层解析逻辑
先解析整个JSON为原始结构,再逐个转换为目标类型:
#[derive(Deserialize)] struct RawParams { params: HashMap<String, RawOrderParameter>, } // 解析入口函数 pub fn parse_order_params(json_str: &str) -> Result<HashMap<String, OrderParameterEnum>, ParseError> { // 先解析为原始结构 let raw: RawParams = serde_json::from_str(json_str)?; let mut result = HashMap::new(); for (key, raw_param) in raw.params { // 根据type字段转换为对应泛型实例 let param = match raw_param.param_type { OrderParameterType::Integer => OrderParameterEnum::Integer(OrderParameter::<isize>::from_raw(raw_param)?), OrderParameterType::Decimal => OrderParameterEnum::Decimal(OrderParameter::<Decimal>::from_raw(raw_param)?), }; result.insert(key, param); } Ok(result) }
原方案问题解析
- 泛型约束冲突:直接为
OrderParameter<S>实现Deserialize时,添加S: Deserialize<'de>会导致实现的约束比Deserializetrait本身更严格(trait没有要求S必须可反序列化),因此编译报错。 - 类型逻辑混乱:原代码试图在泛型实现中根据
type字段强制转换choices的类型,这违背了泛型的编译时类型检查原则,且未初始化choices就调用is_some()会导致编译错误。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

