如何用同一类反序列化属性名不同的XML?
解决XmlSerializer动态适配不同元素名的反序列化问题
问题根源分析
你当前的代码存在三个核心问题:
- 错误使用[XmlRoot]特性:Entity和Entities类并非XML的根节点,添加[XmlRoot]会干扰序列化器的节点识别逻辑。
- 未处理命名空间属性:
xlink:href属于http://www.w3.org/1999/xlink命名空间,直接映射href属性会导致无法匹配,最终得到空值。 - 硬编码元素名:固定的元素名映射无法适配
entities/entity、factories/factory这类同结构不同名称的XML节点。
解决方案:动态修改序列化规则
通过XmlAttributeOverrides可以在运行时动态指定元素名称,无需为每个结构重复创建类。以下是完整实现步骤:
1. 修正基础类定义
首先调整类的特性配置,移除错误的[XmlRoot],并正确处理xlink命名空间:
// 通用的实体基类,适配所有同结构的子节点 public class EntityBase { [XmlAttribute(AttributeName = "id")] public int Id { get; set; } // 明确指定xlink命名空间 [XmlAttribute(AttributeName = "href", Namespace = "http://www.w3.org/1999/xlink")] public string Href { get; set; } } // 通用的容器类,用于包裹子节点列表 public class EntityContainer { [XmlElement] public List<EntityBase> Items { get; set; } } // 根节点类,仅保留必要配置 [XmlRoot(ElementName = "base")] public class BaseRoot { [XmlElement] public EntityContainer Container { get; set; } // 注意:xmlns:xlink是XML命名空间声明,不属于元素属性,无需手动映射,XmlSerializer会自动处理 }
2. 动态创建序列化器
编写方法根据传入的容器元素名(如entities、factories)和子元素名(如entity、factory)生成对应的序列化器:
public static XmlSerializer CreateDynamicSerializer(string containerElementName, string itemElementName) { var overrides = new XmlAttributeOverrides(); // 覆盖BaseRoot中Container属性的XmlElement名称 var containerAttrs = new XmlAttributes(); containerAttrs.XmlElements.Add(new XmlElementAttribute(containerElementName)); overrides.Add(typeof(BaseRoot), "Container", containerAttrs); // 覆盖EntityContainer中Items属性的XmlElement名称 var itemAttrs = new XmlAttributes(); itemAttrs.XmlElements.Add(new XmlElementAttribute(itemElementName, typeof(EntityBase))); overrides.Add(typeof(EntityContainer), "Items", itemAttrs); return new XmlSerializer(typeof(BaseRoot), overrides); }
3. 反序列化示例
使用动态生成的序列化器处理不同结构的XML:
// 处理entities结构的XML string entitiesXml = @"<?xml version=""1.0"" encoding=""UTF-8""?> <base xmlns:xlink=""http://www.w3.org/1999/xlink""> <entities> <entity id=""1"" xlink:href=""http://localhost""/> <entity id=""2"" xlink:href=""http://localhost""/> </entities> </base>"; using (var reader = new StringReader(entitiesXml)) { var serializer = CreateDynamicSerializer("entities", "entity"); var result = (BaseRoot)serializer.Deserialize(reader); // result.Container.Items 包含两个EntityBase对象,属性正确映射 } // 处理factories结构的XML string factoriesXml = @"<?xml version=""1.0"" encoding=""UTF-8""?> <base xmlns:xlink=""http://www.w3.org/1999/xlink""> <factories> <factory id=""1"" xlink:href=""http://localhost""/> <factory id=""2"" xlink:href=""http://localhost""/> </factories> </base>"; using (var reader = new StringReader(factoriesXml)) { var serializer = CreateDynamicSerializer("factories", "factory"); var result = (BaseRoot)serializer.Deserialize(reader); // result.Container.Items 包含两个EntityBase对象,属性正确映射 }
额外优化建议
- 缓存序列化器:每次通过
XmlAttributeOverrides创建序列化器都会生成动态程序集,频繁创建会影响性能。建议用字典缓存不同containerElementName+itemElementName组合对应的序列化器实例。 - 空值处理:在反序列化后检查
Container和Items是否为null,避免空引用异常。
内容的提问来源于stack exchange,提问作者Terence
相关产品推荐
相关产品推荐

