如何序列化含大量子类的抽象类?优化XML序列化结构
实现子类类型直接作为XML元素名的序列化方案
问题描述
序列化包含子类字段的类时,XML中总是显示父类字段名(如child),而非直接使用子类类型名作为元素名。现有C#类结构如下:
public class CustomExpression { [XmlAttribute] public string callFormat; [XmlAttribute] public string format; [XmlAttribute] public string name; [XmlAttribute] public string style; public VzExpression child; /* constructor and methods */ } [XmlInclude(typeof(BinaryOp)) /* other subclasses */] public abstract class VzExpression {} public class BinaryOp : VzExpression { public VzExpression left; public VzExpression right; /* constructor and methods */ } /* And some more subclasses */
当前序列化结果:
<CustomExpression callFormat="" format="" name="" style=""> <child xsi:type="BinaryOp" ...> <left ... /> <right ... /> </child> </CustomExpression>
期望序列化结果(直接用子类类型替代父类字段名作为XML元素名):
<CustomExpression callFormat="" format="" name="" style=""> <BinaryOp ...> <VzExpression ... /> <VzExpression ... /> </BinaryOp> </CustomExpression>
曾尝试给字段添加[XmlElement("BinaryOp", typeof(BinaryOp))]属性,但子类约有10个,不想每次使用VzExpression都添加大量此类属性,寻求更简洁的实现方式。
解决方案
方法1:使用XmlAttributeOverrides动态配置元素映射
无需修改现有实体类属性,在创建XmlSerializer时动态指定父类字段对应的子类元素名,一次性配置所有子类,后续复用序列化器即可。
示例代码:
// 收集所有VzExpression子类的类型 var subTypes = new[] { typeof(BinaryOp), /* 其他子类类型 */ }; // 创建配置对象 var overrides = new XmlAttributeOverrides(); var xmlAttrs = new XmlAttributes(); // 为每个子类添加XmlElement映射,元素名使用类名 foreach (var type in subTypes) { xmlAttrs.XmlElements.Add(new XmlElementAttribute(type.Name, type)); } // 将配置应用到CustomExpression的child字段 overrides.Add(typeof(CustomExpression), nameof(CustomExpression.child), xmlAttrs); // 创建配置好的XmlSerializer(建议缓存该实例,避免重复创建导致内存泄漏) var serializer = new XmlSerializer(typeof(CustomExpression), overrides); // 序列化示例 using (var writer = new StringWriter()) { serializer.Serialize(writer, customExpressionInstance); var resultXml = writer.ToString(); }
此方法会让child字段直接以子类类名作为XML元素名,替代原有的child,且自动识别子类类型,无需在实体类中添加大量属性。
方法2:XmlChoiceIdentifier配合枚举(适合需严格控制元素名场景)
若需要自定义元素名(与类名不一致),可通过枚举配合XmlChoiceIdentifier实现:
- 定义对应子类的枚举:
public enum VzExpressionElementType { BinaryOp, // 其他子类对应的枚举值 }
- 修改
CustomExpression类:
public class CustomExpression { [XmlAttribute] public string callFormat; [XmlAttribute] public string format; [XmlAttribute] public string name; [XmlAttribute] public string style; [XmlChoiceIdentifier(nameof(ChildElementType))] [XmlElement(typeof(BinaryOp), ElementName = nameof(VzExpressionElementType.BinaryOp))] // 为每个子类添加对应的XmlElement属性 public VzExpression child; [XmlIgnore] public VzExpressionElementType ChildElementType { get; set; } // 赋值child时同步设置枚举值 public void AssignChild(VzExpression child) { this.child = child; ChildElementType = Enum.Parse<VzExpressionElementType>(child.GetType().Name); } }
此方法仍需为每个子类添加XmlElement属性,更适合元素名与类名不同的场景,子类较多时推荐使用方法1。
注意事项
- 使用
XmlAttributeOverrides创建XmlSerializer时,需缓存序列化器实例,避免重复创建导致内存泄漏(不同配置会生成新的动态程序集)。 - 确保所有子类已通过
[XmlInclude]标记在VzExpression上,或在创建XmlSerializer时通过extraTypes参数传入。
内容的提问来源于stack exchange,提问作者SSave
相关产品推荐
相关产品推荐

