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

如何在serde(json5)中支持JSON的变体类型字段?

解决Serde JSON5解析变体类型字段的问题

你遇到的错误是因为默认情况下Serde会把枚举当成带标签的结构解析——它期望JSON里出现"Single"或"Multi"这样的变体标识字段,但你的输入是直接的字符串或数组,自然匹配失败。下面是两种可行的解决方案:

方案1:使用#[serde(untagged)]属性(推荐)

这是最简单的方法,只需给枚举添加#[serde(untagged)]属性,告诉Serde不要寻找变体标签,而是按顺序尝试将输入值匹配到枚举的变体上:

use serde::Deserialize;
use json5;

#[derive(Deserialize, Debug, PartialEq)]
#[serde(untagged)]
enum StringOrStrings {
    Single(String),
    Multi(Vec<String>),
}

#[derive(Deserialize, Debug, PartialEq)]
struct Config {
    message: StringOrStrings,
    n: i32,
}

fn main() {
    // 解析单个字符串的情况
    let single_input = r#"{ "message": "hello world", "n": 42 }"#;
    let single_config: Config = json5::from_str(single_input).unwrap();
    assert_eq!(single_config.message, StringOrStrings::Single("hello world".to_string()));

    // 解析字符串数组的情况
    let multi_input = r#"{ "message": ["hello", "world", "foo"], "n": 100 }"#;
    let multi_config: Config = json5::from_str(multi_input).unwrap();
    assert_eq!(multi_config.message, StringOrStrings::Multi(vec!["hello".to_string(), "world".to_string(), "foo".to_string()]));
}

Serde会先尝试将输入匹配第一个变体(Single(String)),如果输入是字符串则成功;如果失败,再尝试匹配第二个变体(Multi(Vec<String>)),如果是字符串数组则成功。

方案2:手动实现Deserialize trait(自定义控制)

如果需要更精细的逻辑(比如自定义错误提示、校验数组元素类型等),可以手动实现Deserialize trait:

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

#[derive(Debug, PartialEq)]
enum StringOrStrings {
    Single(String),
    Multi(Vec<String>),
}

struct StringOrStringsVisitor;

impl<'de> Visitor<'de> for StringOrStringsVisitor {
    type Value = StringOrStrings;

    // 自定义解析失败时的提示信息
    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a single string or an array of strings")
    }

    // 处理单个字符串的情况
    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        Ok(StringOrStrings::Single(value.to_string()))
    }

    // 处理字符串数组的情况
    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        let mut strings = Vec::new();
        while let Some(s) = seq.next_element()? {
            strings.push(s);
        }
        Ok(StringOrStrings::Multi(strings))
    }
}

impl<'de> Deserialize<'de> for StringOrStrings {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(StringOrStringsVisitor)
    }
}

#[derive(Deserialize, Debug, PartialEq)]
struct Config {
    message: StringOrStrings,
    n: i32,
}

// 测试代码同方案1

这种方式可以完全控制解析逻辑,比如你可以在visit_seq里添加校验,确保数组里的元素都是字符串类型,或者处理空数组的特殊情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:53:15