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

Rust中使用serde-xml-rs解析混合元素与无标签文本的XML

Rust 解析混合子元素与无标签文本的XML解决方案

报错原因

你遇到的duplicate field $value报错是因为serde-xml-rs的$value字段默认仅支持捕获单个文本节点,而你的示例XML中<element>根标签下存在三个节点:

  1. <subelement>标签前的换行、缩进空白(属于文本节点)
  2. 带id属性的<subelement>元素节点
  3. <subelement>标签后的arbitrary text文本加末尾空白(属于第二个文本节点)
    两个文本节点同时映射到同一个$value字段,就触发了重复字段冲突。

解决方案

方案1:用数组存储所有文本节点后过滤合并

将存储文本的字段修改为Vec<String>类型,serde会自动收集所有零散的文本节点,你可以后续自行过滤空白内容、拼接得到目标文本,实现代码如下:

use serde_derive::{Deserialize, Serialize};
use serde_xml_rs::from_str;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "element")]
struct Element {
    #[serde(default)]
    subelement: Option<SubElement>,
    #[serde(rename = "$value", default)]
    text_parts: Vec<String>,
}

impl Element {
    /// 提取有效文本,自动过滤空白内容
    pub fn get_text(&self) -> String {
        self.text_parts
            .iter()
            .map(|s| s.trim())
            .filter(|s| !s.is_empty())
            .collect::<Vec<_>>()
            .join(" ")
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct SubElement {
    id: i32,
}

fn main() {
    const XML: &str = r#"
    <element>
        <subelement id="123"></subelement>
        arbitrary text
    </element>
    "#;
    let actual: Element = from_str(XML).unwrap();

    // 验证解析结果
    assert_eq!(actual.subelement, Some(SubElement { id: 123 }));
    assert_eq!(actual.get_text(), "arbitrary text");

    println!("解析结果:{:?}", actual);
    println!("提取到的无标签文本:{}", actual.get_text());
}

方案2:枚举适配任意混合内容

如果你的XML存在文本和子元素任意穿插的场景,可以用枚举统一表示不同类型的节点,适配性更强:

use serde_derive::{Deserialize, Serialize};
use serde_xml_rs::from_str;

/// 节点类型枚举,匹配所有可能的子节点和文本
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Node {
    #[serde(rename = "subelement")]
    SubElement(SubElement),
    #[serde(rename = "$value")]
    Text(String),
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "element")]
struct Element {
    #[serde(rename = "$value", default)]
    nodes: Vec<Node>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct SubElement {
    id: i32,
}

// 后续遍历nodes数组即可分别提取子元素和文本内容

可选优化:自动修剪空白

在Cargo.toml中为serde-xml-rs开启trim_text特性,库会自动修剪所有文本节点前后的空白,减少后续过滤的工作量:

serde-xml-rs = { version = "0.9.0", features = ["trim_text"] }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 07:18:04