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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 12:45:36