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

在Rust中使用Serde反序列化动态XML是否可行?

解决方案

可以通过Serde结合serde-xml-rs实现,核心是为Element和VM编写自定义反序列化逻辑,手动处理XML事件流,区分动态的键值对和向量类型,思路和Go中操作解析器的方式类似。

1. 添加依赖

在Cargo.toml中加入以下依赖:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde-xml-rs = "0.8"

2. 定义数据结构

保持你期望的结构,可微调以适配反序列化:

use serde::Deserialize;
use serde_xml_rs::Error;

#[derive(Debug, PartialEq)]
enum Element {
    Pair(String, String),
    Vector(String, Vec<(String, String)>),
}

#[derive(Debug, PartialEq)]
pub struct VM {
    id: i64,
    template: Vec<Element>,
}

3. 实现自定义反序列化逻辑

为Element实现反序列化

手动解析XML事件,判断当前标签是普通键值对还是包含子元素的向量:

impl<'de> Deserialize<'de> for Element {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let mut deserializer = deserializer.into_deserializer();
        let mut elements = Vec::new();
        let mut current_tag = String::new();

        loop {
            match serde_xml_rs::de::next_event(&mut deserializer) {
                Ok(serde_xml_rs::Event::StartElement { name, .. }) => {
                    current_tag = name.local_name;
                }
                Ok(serde_xml_rs::Event::Text(text)) => {
                    // 直接包含文本的标签属于键值对
                    return Ok(Element::Pair(current_tag, text.trim().to_string()));
                }
                Ok(serde_xml_rs::Event::EndElement { name }) => {
                    if name.local_name == current_tag {
                        // 包含子元素的标签属于向量
                        return Ok(Element::Vector(current_tag, elements));
                    }
                }
                Ok(serde_xml_rs::Event::StartElement { name, .. }) => {
                    // 解析向量内的子键值对
                    let key = name.local_name;
                    let mut value = String::new();
                    loop {
                        match serde_xml_rs::de::next_event(&mut deserializer) {
                            Ok(serde_xml_rs::Event::Text(text)) => {
                                value = text.trim().to_string();
                            }
                            Ok(serde_xml_rs::Event::EndElement { name }) => {
                                if name.local_name == key {
                                    elements.push((key, value));
                                    break;
                                }
                            }
                            Err(e) => return Err(serde::de::Error::custom(e)),
                            _ => continue,
                        }
                    }
                }
                Ok(serde_xml_rs::Event::Eof) => break,
                Err(e) => return Err(serde::de::Error::custom(e)),
                _ => continue,
            }
        }

        Err(serde::de::Error::custom("Unexpected end of XML stream"))
    }
}

为VM实现反序列化

通过辅助结构体结合自定义函数解析template内的动态元素列表:

impl<'de> Deserialize<'de> for VM {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct VmHelper {
            id: i64,
            #[serde(deserialize_with = "parse_template_elements")]
            template: Vec<Element>,
        }

        let helper = VmHelper::deserialize(deserializer)?;
        Ok(VM {
            id: helper.id,
            template: helper.template,
        })
    }
}

fn parse_template_elements<'de, D>(deserializer: D) -> Result<Vec<Element>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let mut deserializer = deserializer.into_deserializer();
    let mut elements = Vec::new();

    loop {
        match serde_xml_rs::de::next_event(&mut deserializer) {
            Ok(serde_xml_rs::Event::StartElement { .. }) => {
                let elem = Element::deserialize(&mut deserializer)?;
                elements.push(elem);
            }
            Ok(serde_xml_rs::Event::EndElement { .. }) => break,
            Ok(serde_xml_rs::Event::Eof) => break,
            Err(e) => return Err(serde::de::Error::custom(e)),
            _ => continue,
        }
    }

    Ok(elements)
}

4. 测试代码

验证反序列化结果是否符合预期:

fn main() -> Result<(), Error> {
    let xml = r#"
<vm>
    <id>15</id>
    <template>
        <tag1>content</tag1>
        <tag2>content</tag2>
        <vec1>
            <tag3>content</tag3>
            <tag4>content</tag4>
        </vec1>
        <tag2>content</tag2>
        <vec1>
            <tag3>content</tag3>
        </vec1>
        <vec2>
            <tag3>content</tag3>
        </vec2>
    </template>
</vm>
"#;

    let vm: VM = serde_xml_rs::from_str(xml)?;
    println!("{:#?}", vm);

    // 断言验证核心数据
    assert_eq!(vm.id, 15);
    assert_eq!(vm.template.len(), 6);
    assert!(matches!(vm.template[0], Element::Pair(ref k, ref v) if k == "tag1" && v == "content"));
    assert!(matches!(vm.template[2], Element::Vector(ref k, ref vec) 
        if k == "vec1" && vec == &vec![("tag3".into(), "content".into()), ("tag4".into(), "content".into())]));

    Ok(())
}

关键说明

  • 直接操作XML事件流,能精准区分动态标签的两种类型,和Go中手动控制解析器的逻辑一致
  • 自定义反序列化逻辑可灵活扩展,比如处理空文本、命名空间等边缘场景
  • 数据结构保持简洁,符合你不希望复杂嵌套的要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 14:10:35