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

如何用serde_json扁平化处理联合类型对象数组?

优化Serde反序列化:将Filter数组转换为扁平结构体

需求说明

需要将如下JSON输入:

{
    "filters": [
        {
            "filterType": "MIN_FILTER",
            "min": 2
        },
        {
            "filterType": "MAX_FILTER",
            "max": 10
        },
        {
            "filterType": "PRIORITY_FILTER",
            "priority": "High"
        }
    ]
}

反序列化为对应如下结构的Rust对象:

{
    "filters": {
        "min": 2,
        "max": 10,
        "priority": "High"
    }
}

现有实现的不足

你当前用包含所有可选字段的GenericFilter结构体统一处理不同类型Filter的实现,存在几个明显问题:

  • 类型不安全:每个Filter变体本应只包含专属字段,却被迫携带无关的Option字段
  • 错误处理粗糙:直接用unwrap()会在缺失字段或数据错误时触发panic,无法返回优雅的反序列化错误
  • 代码冗余:遍历数组时需要手动匹配类型并提取字段,重复逻辑多

更优方案1:用枚举实现类型安全的Filter处理

通过定义枚举对应不同Filter类型,每个枚举变体仅包含自身所需字段,再遍历枚举数组合并出最终的Filters结构体:

extern crate serde;
extern crate serde_json;

use serde::{Deserialize, Deserializer, Error};

#[derive(Deserialize, Debug)]
pub struct Object {
    filters: Filters,
}

#[derive(Debug)]
pub struct Filters {
    min: u32,
    max: u32,
    priority: String,
}

#[derive(Deserialize, Debug)]
#[serde(tag = "filterType")]
enum Filter {
    #[serde(rename = "MIN_FILTER")]
    MinFilter { min: u32 },
    #[serde(rename = "MAX_FILTER")]
    MaxFilter { max: u32 },
    #[serde(rename = "PRIORITY_FILTER")]
    PriorityFilter { priority: String },
}

impl<'de> Deserialize<'de> for Filters {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let filters: Vec<Filter> = Vec::deserialize(deserializer)?;
        
        let mut min: Option<u32> = None;
        let mut max: Option<u32> = None;
        let mut priority: Option<String> = None;

        for filter in filters {
            match filter {
                Filter::MinFilter { min: val } => {
                    if min.is_some() {
                        return Err(D::Error::duplicate_field("min"));
                    }
                    min = Some(val);
                }
                Filter::MaxFilter { max: val } => {
                    if max.is_some() {
                        return Err(D::Error::duplicate_field("max"));
                    }
                    max = Some(val);
                }
                Filter::PriorityFilter { priority: val } => {
                    if priority.is_some() {
                        return Err(D::Error::duplicate_field("priority"));
                    }
                    priority = Some(val);
                }
            }
        }

        Ok(Filters {
            min: min.ok_or_else(|| D::Error::missing_field("min"))?,
            max: max.ok_or_else(|| D::Error::missing_field("max"))?,
            priority: priority.ok_or_else(|| D::Error::missing_field("priority"))?,
        })
    }
}

fn main() {
    let json = r#"
        {
            "filters": [
                {
                    "filterType": "MIN_FILTER",
                    "min": 2
                },
                {
                    "filterType": "MAX_FILTER",
                    "max": 10
                },
                {
                    "filterType": "PRIORITY_FILTER",
                    "priority": "High"
                }
            ]
        }
    "#;

    println!(
        "Deserialized = {:#?}",
        serde_json::from_str::<Object>(&json)
    );
}

方案优势

  • 类型安全:每个Filter变体仅包含自身需要的字段,避免无关字段冗余
  • 健壮错误处理:通过ok_or_else返回标准Serde错误,替代unwrap()的panic
  • 扩展性强:新增Filter类型时只需添加对应枚举变体,无需修改通用结构体

更优方案2:直接使用Serde Visitor(极致性能)

如果追求更高性能,避免中间Vec<Filter>的分配,可以直接实现Visitor trait,遍历JSON数组中的每个对象并提取字段:

extern crate serde;
extern crate serde_json;

use serde::{de::Visitor, Deserialize, Deserializer, Error};
use std::fmt;

#[derive(Deserialize, Debug)]
pub struct Object {
    filters: Filters,
}

#[derive(Debug)]
pub struct Filters {
    min: u32,
    max: u32,
    priority: String,
}

impl<'de> Deserialize<'de> for Filters {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct FiltersVisitor;

        impl<'de> Visitor<'de> for FiltersVisitor {
            type Value = Filters;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("an array of filter objects")
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: serde::de::SeqAccess<'de>,
            {
                let mut min: Option<u32> = None;
                let mut max: Option<u32> = None;
                let mut priority: Option<String> = None;

                while let Some(filter_obj) = seq.next_element::<serde_json::Value>()? {
                    let filter_type = filter_obj.get("filterType")
                        .and_then(|v| v.as_str())
                        .ok_or_else(|| A::Error::missing_field("filterType"))?;

                    match filter_type {
                        "MIN_FILTER" => {
                            if min.is_some() {
                                return Err(A::Error::duplicate_field("min"));
                            }
                            min = filter_obj.get("min")
                                .and_then(|v| v.as_u64())
                                .map(|x| x as u32)
                                .ok_or_else(|| A::Error::missing_field("min"));
                        }
                        "MAX_FILTER" => {
                            if max.is_some() {
                                return Err(A::Error::duplicate_field("max"));
                            }
                            max = filter_obj.get("max")
                                .and_then(|v| v.as_u64())
                                .map(|x| x as u32)
                                .ok_or_else(|| A::Error::missing_field("max"));
                        }
                        "PRIORITY_FILTER" => {
                            if priority.is_some() {
                                return Err(A::Error::duplicate_field("priority"));
                            }
                            priority = filter_obj.get("priority")
                                .and_then(|v| v.as_str())
                                .map(|s| s.to_string())
                                .ok_or_else(|| A::Error::missing_field("priority"));
                        }
                        _ => return Err(A::Error::unknown_variant(filter_type, &["MIN_FILTER", "MAX_FILTER", "PRIORITY_FILTER"])),
                    }?;
                }

                Ok(Filters {
                    min: min.ok_or_else(|| A::Error::missing_field("min"))?,
                    max: max.ok_or_else(|| A::Error::missing_field("max"))?,
                    priority: priority.ok_or_else(|| A::Error::missing_field("priority"))?,
                })
            }
        }

        deserializer.deserialize_seq(FiltersVisitor)
    }
}

fn main() {
    let json = r#"
        {
            "filters": [
                {
                    "filterType": "MIN_FILTER",
                    "min": 2
                },
                {
                    "filterType": "MAX_FILTER",
                    "max": 10
                },
                {
                    "filterType": "PRIORITY_FILTER",
                    "priority": "High"
                }
            ]
        }
    "#;

    println!(
        "Deserialized = {:#?}",
        serde_json::from_str::<Object>(&json)
    );
}

方案优势

  • 极致性能:直接操作JSON值,避免中间枚举结构体的分配和序列化开销
  • 精细控制:可直接处理JSON原始结构,适合对性能敏感的场景
  • 完整错误处理:对缺失字段、重复字段、未知类型都能返回精准错误信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 23:05:33