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

如何用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())
    }
}

遇到的问题

  1. 编译器提示需为S添加Deserialize<'_>约束,但添加后会出现“impl要求比trait严格”的错误。
  2. 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)
}

原方案问题解析

  1. 泛型约束冲突:直接为OrderParameter<S>实现Deserialize时,添加S: Deserialize<'de>会导致实现的约束比Deserialize trait本身更严格(trait没有要求S必须可反序列化),因此编译报错。
  2. 类型逻辑混乱:原代码试图在泛型实现中根据type字段强制转换choices的类型,这违背了泛型的编译时类型检查原则,且未初始化choices就调用is_some()会导致编译错误。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:10:28