quickxml+serde序列化正常但无法反序列化多类型XML元素问题
解决quickxml+serde反序列化Eclipse启动配置XML时属性为空的问题
问题分析
你的代码能正常序列化生成符合结构的XML,但反序列化时attrs字段为空,核心原因有两个:
- 根节点名称不匹配:结构体
EclipseLaunchConfiguration默认序列化的根节点是<EclipseLaunchConfiguration>,但实际Eclipse XML根节点是<launchConfiguration>,导致反序列化上下文不匹配。 - 未指定子元素映射规则:serde默认不会自动将根节点下的直接子元素(
booleanAttribute/stringAttribute等)绑定到attrs字段,需要明确告诉quickxml如何捕获这些子元素。
解决方案
修改代码中的两处关键配置:
1. 修正根节点名称映射
给EclipseLaunchConfiguration结构体添加#[serde(rename = "launchConfiguration")]注解,让序列化/反序列化的根节点与实际XML一致。
2. 绑定子元素到attrs字段
给attrs字段添加#[serde(rename = "$value")]注解,该标记在quickxml的serde实现中代表当前元素的所有子内容(包括子元素),这样所有booleanAttribute/stringAttribute等子元素都会被反序列化为Vec<ConfigAttribute>。
修改后的完整代码
use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename = "launchConfiguration")] // 匹配实际XML根节点名称 struct EclipseLaunchConfiguration { #[serde(rename = "@type")] configuration_type: String, #[serde(rename = "$value")] // 将根节点下的所有子元素映射到attrs #[serde(default)] attrs: Vec<ConfigAttribute>, } #[derive(Debug, Serialize, Deserialize)] enum ConfigAttribute { #[serde(rename = "booleanAttribute")] Bool { #[serde(rename = "@key")] key: String, #[serde(rename = "@value")] value: bool, }, #[serde(rename = "stringAttribute")] Str { #[serde(rename = "@key")] key: String, #[serde(rename = "@value")] value: String, }, #[serde(rename = "listAttribute")] List{ #[serde(rename = "@key")] key: String, #[serde(rename = "listEntry")] list_entry: Vec<ListEntry>, }, #[serde(rename = "mapAttribute")] Map{ #[serde(rename = "@key")] key: String, #[serde(rename = "listEntry")] // 若实际Eclipse用mapEntry则改为mapEntry map_entry: Vec<MapEntry>, }, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] struct ListEntry { #[serde(rename = "@value")] value: String, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] struct MapEntry { #[serde(rename = "@key")] key: String, #[serde(rename = "@value")] value: String, } #[cfg(test)] mod tests { use super::*; #[test] fn test_to_xml() -> anyhow::Result<()> { let config = EclipseLaunchConfiguration { configuration_type: "whatever".to_string(), attrs: vec![ ConfigAttribute::Bool{ key: "ok".to_string(), value: true, }, ConfigAttribute::Str { key: "hello".to_string(), value: "world".to_string(), }, ConfigAttribute::List{ key: "list".to_string(), list_entry: vec![ ListEntry { value: "one".to_string(), }, ListEntry { value: "two".to_string(), }, ], }, ConfigAttribute::Map { key: "map".to_string(), map_entry: vec![ MapEntry { key: "one".to_string(), value: "1".to_string(), }, MapEntry { key: "two".to_string(), value: "2".to_string(), }, ], } ], }; let xml = quick_xml::se::to_string(&config)?; println!("{}", xml); let config2: EclipseLaunchConfiguration = quick_xml::de::from_str(&xml)?; println!("{:#?}", config2); // 验证反序列化结果与原数据一致 assert_eq!(config.attrs.len(), config2.attrs.len()); Ok(()) } }
验证结果
修改后运行测试,反序列化得到的config2.attrs会包含所有属性元素,长度与原数据一致,且每个元素的内容正确匹配。
内容的提问来源于stack exchange,提问作者Petr Kozelka
相关产品推荐
相关产品推荐

