如何用quick_xml与serde将XML字段直接反序列化为同名枚举单元变体
解决方案
要将XML中的字符串直接反序列化为Rust单元枚举变体,核心问题在于serde默认不会将纯字符串映射到单元枚举——它默认期望枚举以结构化对象的形式出现(比如{"IEEE754LSBSingle": {}})。以下是两种无需手动编写大量匹配逻辑的可行方案:
方案1:使用strum库自动生成转换代码
strum库可以自动为枚举生成字符串与变体的转换逻辑,无需手动编写匹配分支。
步骤1:添加依赖
在Cargo.toml中添加:
[dependencies] # 保留原有依赖,新增strum strum = { version = "0.25", features = ["derive", "serde"] }
步骤2:修改代码
为DataTypes添加自动转换逻辑:
use binary_type_cast::PrettyPrint; use quick_xml::{de::from_reader, DeError}; use serde::{Deserialize, Deserializer}; use std::{fs::File, io::BufReader}; use strum::EnumString; #[derive(Clone, Copy, Debug, EnumString)] // XML字符串与枚举变体名称完全匹配,无需额外命名规则配置 pub enum DataTypes { IEEE754LSBSingle, IEEE754LSBDouble, IEEE754LSBSingleArr, IEEE754LSBDoubleArr, ASCIIString, } // 为DataTypes实现serde反序列化适配 impl<'de> Deserialize<'de> for DataTypes { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; Self::from_str(&s).map_err(|e| serde::de::Error::custom(e.to_string())) } } #[derive(Debug, Deserialize)] pub struct Records { #[serde(rename="Record")] pub records: Vec<Record>, } #[derive(Debug, Deserialize)] pub struct Record { pub name: String, pub number: u32, pub location: u32, pub data_type: DataTypes, pub length: u32 } pub fn get_xml_record(file: &File) -> Result<Records, DeError> { let reader = BufReader::new(file); let records: Records = from_reader(reader)?; Ok(records) } fn main() { let description = "./data/desc.xml"; if let Ok(file) = File::open(description) { if let Ok(records) = get_xml_record(&file) { println!("{:#?}",records); } } }
方案2:手动实现轻量转换逻辑
如果不想引入额外依赖,可以手动实现TryFrom<String> trait,代码量极小:
use binary_type_cast::PrettyPrint; use quick_xml::{de::from_reader, DeError}; use serde::{Deserialize}; use std::{fs::File, io::BufReader}; #[derive(Clone, Copy, Debug, Deserialize)] pub enum DataTypes { IEEE754LSBSingle, IEEE754LSBDouble, IEEE754LSBSingleArr, IEEE754LSBDoubleArr, ASCIIString, } // 手动实现字符串到枚举的转换 impl TryFrom<String> for DataTypes { type Error = DeError; fn try_from(s: String) -> Result<Self, Self::Error> { match s.as_str() { "IEEE754LSBSingle" => Ok(Self::IEEE754LSBSingle), "IEEE754LSBDouble" => Ok(Self::IEEE754LSBDouble), "IEEE754LSBSingleArr" => Ok(Self::IEEE754LSBSingleArr), "IEEE754LSBDoubleArr" => Ok(Self::IEEE754LSBDoubleArr), "ASCIIString" => Ok(Self::ASCIIString), _ => Err(DeError::Custom(format!("未知数据类型: {}", s))), } } } #[derive(Debug, Deserialize)] pub struct Records { #[serde(rename="Record")] pub records: Vec<Record>, } #[derive(Debug, Deserialize)] pub struct Record { pub name: String, pub number: u32, pub location: u32, #[serde(try_from = "String")] // 告诉serde使用TryFrom转换 pub data_type: DataTypes, pub length: u32 } pub fn get_xml_record(file: &File) -> Result<Records, DeError> { let reader = BufReader::new(file); let records: Records = from_reader(reader)?; Ok(records) } fn main() { let description = "./data/desc.xml"; if let Ok(file) = File::open(description) { if let Ok(records) = get_xml_record(&file) { println!("{:#?}",records); } } }
为什么之前的属性不生效?
你尝试的#[serde(tag="data_type")]和#[serde(untagged)]都是针对结构化枚举反序列化的配置:
#[serde(tag="data_type")]期望XML中存在标记枚举类型的嵌套结构;#[serde(untagged)]用于消除枚举标签,但依然要求变体是结构化数据,而非纯字符串。
这两种配置都不匹配你当前XML中纯字符串的data_type格式,因此无法生效。
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

