反序列化时如何将多个XML属性映射到单个C#属性?
问题描述
我需要用XmlSerializer反序列化XML时,让单个C#属性BloodKind能映射到XML元素的多个属性名(比如blooded或bloodtype)。但[XmlAttribute]不允许多个特性叠加,[XmlChoiceIdentifier]也不支持属性场景;如果为每个属性名单独创建C#属性,会导致类臃肿且需要大量空值检查,而且我无法修改源XML结构。
示例XML:
<Animal blooded="warm"> <!-- 属性可能是"blooded"、"bloodtype"等 --> <Horse>Jack</Horse> <Chicken>Charlie</Chicken> </Animal>
期望的C#类写法(但无法直接实现):
[Serializable] public partial class Animal { public string Horse { get; set; } public string Chicken { get; set; } [XmlAttribute("blooded")] [XmlAttribute("bloodtype")] // 希望支持多个属性名映射到同一个属性 public string BloodKind { get; set; } }
方案一:使用XmlAttributeOverrides动态构建序列化器
通过XmlAttributeOverrides可以为同一个C#属性动态注册多个XML属性名称,无需在类中定义冗余属性,序列化器会自动处理多属性映射。
代码示例:
[Serializable] public partial class Animal { public string Horse { get; set; } public string Chicken { get; set; } public string BloodKind { get; set; } } public class Program { public static void Main(string[] args) { // 创建属性覆盖配置 var overrides = new XmlAttributeOverrides(); var attrs = new XmlAttributes(); // 为BloodKind添加多个XML属性映射 attrs.XmlAttributes.Add(new XmlAttributeAttribute("blooded")); attrs.XmlAttributes.Add(new XmlAttributeAttribute("bloodtype")); overrides.Add(typeof(Animal), nameof(Animal.BloodKind), attrs); // 使用覆盖配置创建序列化器 XmlSerializer serializer = new XmlSerializer(typeof(Animal), overrides); using Stream reader = new FileStream("data.xml", FileMode.Open); Animal animal = (Animal)serializer.Deserialize(reader); Console.WriteLine(animal.BloodKind); // 输出"warm" } }
注意:若多次创建相同配置的XmlSerializer,建议缓存实例,避免因重复生成动态程序集导致内存泄漏。
方案二:实现IXmlSerializable接口手动处理属性
如果需要更灵活的控制(比如属性名数量较多或需自定义逻辑),可以实现IXmlSerializable接口,手动读取XML属性值。
代码示例:
public class Animal : IXmlSerializable { public string Horse { get; set; } public string Chicken { get; set; } public string BloodKind { get; set; } public System.Xml.Schema.XmlSchema GetSchema() => null; public void ReadXml(System.Xml.XmlReader reader) { // 遍历所有属性,匹配目标名称 while (reader.MoveToNextAttribute()) { if (reader.Name is "blooded" or "bloodtype") { BloodKind = reader.Value; } } // 回到元素节点,读取子元素 reader.MoveToElement(); reader.ReadStartElement(); while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { switch (reader.Name) { case "Horse": Horse = reader.ReadElementContentAsString(); break; case "Chicken": Chicken = reader.ReadElementContentAsString(); break; default: reader.Skip(); // 跳过未知元素 break; } } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { // 序列化时选择输出其中一个属性名(比如"blooded") if (!string.IsNullOrEmpty(BloodKind)) { writer.WriteAttributeString("blooded", BloodKind); } if (!string.IsNullOrEmpty(Horse)) { writer.WriteElementString("Horse", Horse); } if (!string.IsNullOrEmpty(Chicken)) { writer.WriteElementString("Chicken", Chicken); } } } public class Program { public static void Main(string[] args) { XmlSerializer serializer = new XmlSerializer(typeof(Animal)); using Stream reader = new FileStream("data.xml", FileMode.Open); Animal animal = (Animal)serializer.Deserialize(reader); Console.WriteLine(animal.BloodKind); // 输出"warm" } }
该方案完全自定义XML读写逻辑,不受默认XmlSerializer特性限制,适合复杂映射场景。
内容的提问来源于stack exchange,提问作者Hysperr
相关产品推荐
相关产品推荐

