使用Rust quick-xml反序列化无序混合字段的问题求助
问题描述
给定如下XML数据:
<Root name="aaa"> <Field1> <Foo name=""/> </Field1> <Field2> <Bar name=""/> </Field2> <Field2> <Bar name=""/> </Field2> <Field1> <Foo name=""/> </Field1> <Field1> <Foo name=""/> </Field1> </Root>
希望将其反序列化为类似如下的Rust结构:
struct Root { name: String fields: Option<Vec<FieldType>> } enum FieldType { Field1(Field1Type) Field2(Field2Type) }
尝试了如下实现,但出现quick-xml错误:Duplicate field 'fields'
#[derive(Debug, Deserialize)] struct Root { name: String, #[serde(alias = "Field1", alias = "Field2")] fields: Option<Vec<FieldType>>, } #[derive(Debug, Deserialize)] enum FieldType { #[serde(rename = "Field1")] field1(Field1), #[serde(rename = "Field2")] field1(Field2), } #[derive(Debug, Deserialize)] struct Field1 { #[serde(rename = "Foo")] foo: String } #[derive(Debug, Deserialize)] struct Field1 { #[serde(rename = "Bar")] bar: String }
问题原因
- 枚举变体重名:代码中
FieldType的两个变体都命名为field1,属于语法错误; #[serde(alias)]用法错误:该属性是给单个字段设置不同的名称别名,无法将多个不同的XML元素合并到同一个集合中;- XML属性处理错误:未使用
@前缀捕获元素属性(如Root的name属性、Foo/Bar的name属性); - 结构体定义错误:
Field1/Field2中的Foo/Bar是XML子元素,不是直接的字符串类型。
解决方案
以下是正确的实现代码:
use quick_xml::de::Deserializer; use serde::Deserialize; use std::io::Read; #[derive(Debug, Deserialize)] struct Root { // 用@前缀捕获XML元素的属性 #[serde(rename = "@name")] name: String, // flatten会把所有匹配FieldType的子元素收集到这个Vec中 #[serde(flatten)] fields: Vec<FieldType>, } #[derive(Debug, Deserialize)] // untagged让serde自动根据XML元素名判断枚举变体 #[serde(untagged)] enum FieldType { #[serde(rename = "Field1")] Field1(Field1), #[serde(rename = "Field2")] Field2(Field2), } #[derive(Debug, Deserialize)] struct Field1 { #[serde(rename = "Foo")] foo: Foo, } #[derive(Debug, Deserialize)] struct Foo { #[serde(rename = "@name")] name: String, } #[derive(Debug, Deserialize)] struct Field2 { #[serde(rename = "Bar")] bar: Bar, } #[derive(Debug, Deserialize)] struct Bar { #[serde(rename = "@name")] name: String, } // 测试反序列化 fn main() -> Result<(), Box<dyn std::error::Error>> { let xml_data = r#" <Root name="aaa"> <Field1> <Foo name=""/> </Field1> <Field2> <Bar name=""/> </Field2> <Field2> <Bar name=""/> </Field2> <Field1> <Foo name=""/> </Field1> <Field1> <Foo name=""/> </Field1> </Root> "#; let mut deserializer = Deserializer::from_str(xml_data); let root: Root = Deserialize::deserialize(&mut deserializer)?; println!("{:#?}", root); Ok(()) }
关键说明
#[serde(rename = "@name")]:quick-xml要求用@前缀来标识XML元素的属性,区别于子元素;#[serde(flatten)]:将所有能匹配FieldType枚举的子元素扁平化收集到fields向量中,避免重复字段的错误;#[serde(untagged)]:让serde无需额外标签,直接根据XML元素的名称(Field1/Field2)自动选择对应的枚举变体;- 修正了结构体定义:将
Foo/Bar定义为独立结构体,正确捕获它们的name属性。
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

