如何让C#类兼容多xsi:type的同名XML标签反序列化
处理XML反序列化中带不同xsi:type的同名标签问题
你遇到的这个情况很典型——同一个XML标签但通过xsi:type区分不同的数据类型,要让C#的XmlSerializer正确识别并反序列化,咱们可以通过基类+派生类+XmlInclude特性的组合来搞定,具体步骤如下:
第一步:定义对应不同xsi:type的派生类
首先,给两种field-value类型分别创建对应的类,同时定义一个基类作为它们的父类:
// 基类,不需要XmlType特性,具体类型由派生类指定 public abstract class FieldValueBase { } // 对应xsi:type="field-text-valueType"的文本类型 [XmlType("field-text-valueType", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public class TextFieldValue : FieldValueBase { [XmlElement(ElementName = "value", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public string Value { get; set; } } // 对应xsi:type="field-date-valueType"的日期类型 [XmlType("field-date-valueType", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public class DateFieldValue : FieldValueBase { [XmlElement(ElementName = "value", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public DateTime Value { get; set; } }
第二步:在父级模型中添加XmlInclude特性指定派生类型
接下来定义包含field标签的类,并且在最上层的根模型类上添加XmlInclude特性,告诉XmlSerializer要识别哪些派生类型:
// 定义<field>对应的类 [XmlType("field", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public class Field { [XmlAttribute("name")] public string Name { get; set; } // 用基类FieldValueBase作为属性类型,兼容两种派生类型 [XmlElement(ElementName = "field-value", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public FieldValueBase FieldValue { get; set; } } // 最上层的<user-defined-data-row>类,必须添加XmlInclude声明派生类型 [XmlRoot(ElementName = "user-defined-data-row", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] [XmlInclude(typeof(TextFieldValue))] [XmlInclude(typeof(DateFieldValue))] public class UserDefinedDataRow { [XmlElement(ElementName = "field", Namespace = "http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0")] public List<Field> Fields { get; set; } }
第三步:执行反序列化操作
现在就可以正常反序列化你的XML了,注意XML里要包含xsi命名空间声明(比如xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"),XmlSerializer会自动根据xsi:type匹配对应的派生类:
string xmlContent = @"<user-defined-data-row xmlns=""http://www.crsoftwareinc.com/xml/ns/titanium/common/v1_0"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""> <field name=""entity""> <field-value xsi:type=""field-text-valueType""> <value>Test</value> </field-value> </field> <field name=""expiry_date""> <field-value xsi:type=""field-date-valueType""> <value>2001-10-07</value> </field-value> </field> </user-defined-data-row>"; XmlSerializer serializer = new XmlSerializer(typeof(UserDefinedDataRow)); using (StringReader reader = new StringReader(xmlContent)) { UserDefinedDataRow result = (UserDefinedDataRow)serializer.Deserialize(reader); // 处理反序列化结果,通过类型判断区分不同字段类型 foreach (var field in result.Fields) { if (field.FieldValue is TextFieldValue textField) { Console.WriteLine($"文本字段 {field.Name}: {textField.Value}"); } else if (field.FieldValue is DateFieldValue dateField) { Console.WriteLine($"日期字段 {field.Name}: {dateField.Value:yyyy-MM-dd}"); } } }
关键注意事项
- 所有类的
Namespace必须和XML中的默认命名空间完全一致,否则序列化器会识别不到标签 XmlInclude特性必须加在直接参与反序列化的根类或父类上,确保序列化器能加载到所有派生类型- 反序列化后,可通过
is关键字判断类型,进而处理对应的数据
内容的提问来源于stack exchange,提问作者OHMR
相关产品推荐
相关产品推荐

