Serde无法识别未标记枚举变体:data did not match any variant错误排查
问题场景
尝试用Serde解析如下JSON数据:
{ "data_type": "uint8", "scales": [{ "encoding": "jpeg", "key": "4.0x4.0x33.0" }] }
反序列化为以下Rust类型:
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct DatasetAttributes { data_type: String, scales: Vec<ScaleEntry>, } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct ScaleEntry { pub encoding: CompressionType, pub key: String, } #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] #[serde(rename_all = "lowercase")] #[serde(untagged)] pub enum CompressionType { Raw(RawCompression), Jpeg(JpegCompression), } #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] #[serde(rename_all = "lowercase")] pub struct JpegCompression { quality: i32, } #[derive(Clone, Serialize, Deserialize, PartialEq, Debug, Default)] pub struct RawCompression;
执行时触发错误:
Error: data did not match any variant of untagged enum CompressionType
疑问:是否误解了Serde未标记枚举的处理逻辑?还是代码存在问题?
问题原因
核心问题是未标记枚举的结构与JSON数据格式不匹配:
当前CompressionType是未标记枚举,Serde会尝试将encoding字段的内容直接匹配枚举变体的结构。但JSON中encoding是单纯的字符串"jpeg",而你的Jpeg变体对应包含quality字段的JpegCompression结构体,Raw变体对应空结构体RawCompression——两者都无法匹配单纯的字符串值,因此触发错误。
解决方法
根据你的需求,可以选择以下三种方案之一:
方案1:简化枚举,直接匹配字符串
如果JpegCompression和RawCompression不需要存储额外参数,直接将枚举变体改为对应字符串:
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] #[serde(rename_all = "lowercase")] pub enum CompressionType { Raw, Jpeg, }
此时JSON中的"jpeg"会直接匹配CompressionType::Jpeg,"raw"匹配CompressionType::Raw,无需额外结构体。
方案2:调整JSON结构,适配嵌套对象
如果需要保留结构体存储额外参数(比如quality),需将JSON的encoding字段改为嵌套对象:
{ "data_type": "uint8", "scales": [{ "encoding": { "jpeg": { "quality": 90 } }, "key": "4.0x4.0x33.0" }] }
未标记枚举会自动识别嵌套的对象结构,匹配对应的变体和结构体参数。
方案3:自定义反序列化逻辑
如果无法修改现有JSON格式,需给CompressionType实现自定义反序列化,手动将字符串映射到对应变体:
use serde::{Deserialize, Deserializer}; #[derive(Clone, Serialize, PartialEq, Debug)] #[serde(rename_all = "lowercase")] pub enum CompressionType { Raw(RawCompression), Jpeg(JpegCompression), } #[derive(Clone, Serialize, PartialEq, Debug, Default)] pub struct RawCompression; #[derive(Clone, Serialize, PartialEq, Debug)] #[serde(rename_all = "lowercase")] pub struct JpegCompression { quality: i32, } impl<'de> Deserialize<'de> for CompressionType { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; match s.as_str() { "raw" => Ok(CompressionType::Raw(RawCompression)), "jpeg" => Ok(CompressionType::Jpeg(JpegCompression { quality: 80 })), // 可根据需求设置默认值或调整逻辑 other => Err(serde::de::Error::unknown_variant(other, &["raw", "jpeg"])), } } }
这种方式兼容原有JSON格式,同时保留结构体的扩展能力。
内容的提问来源于stack exchange,提问作者tomka

