如何在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
相关产品推荐
相关产品推荐

