C# XmlSerializer处理含xsi:type属性XML序列化反序列化问题
问题核心原因
.NET 内置XmlSerializer对xsi:type属性有特殊的内置处理逻辑:它默认将该属性作为多态类型判定的标识,不会将其作为普通自定义属性进行读写。直接在类中声明xsi命名空间下的type属性会被序列化器忽略,甚至触发"未找到对应类型"的校验报错,这是所有常规特性配置方案无效的根本原因。
除此之外你的原有代码还有两个会导致序列化结果不符的问题:
- 所有子类未指定和根节点一致的默认命名空间,序列化时会自动生成多余的
xmlns=""声明 ST_Value类的字段定义错误:目标XML中Value节点下直接排列Field子节点,没有外层数组包裹,且叶子节点Value存在value属性存储具体值,原有代码未覆盖这部分逻辑
可直接运行的修正代码
第一步:修正实体类定义
所有需要xsi:type属性的类(ST_Data、ST_Field)通过实现IXmlSerializable接口手动控制属性读写,完全绕开序列化器对xsi:type的内置拦截,同时兼容array/collection两种属性值:
private ST_DtxInstance dtxFile = new ST_DtxInstance(); private const string DefaultNs = "http://www.staubli.com/robotics/VAL3/Data/2"; private const string XsiNs = "http://www.w3.org/2001/XMLSchema-instance"; [XmlRoot(ElementName = "Database", Namespace = DefaultNs, IsNullable = false)] public class ST_DtxInstance { [XmlArray("Datas", Namespace = DefaultNs)] [XmlArrayItem("Data", Namespace = DefaultNs)] public ST_Data[] csDatas { get; set; } = new ST_Data[] { new ST_Data(), new ST_Data() }; } public class ST_Data : IXmlSerializable { [XmlElement("Value", Namespace = DefaultNs)] public ST_Value value { get; set; } = new ST_Value(); [XmlAttribute("name")] public string csName { get; set; } = "Name"; [XmlAttribute("access")] public string csAccess { get; set; } = "public"; // 支持array/collection两种取值 [XmlIgnore] public string xsi_xmlType { get; set; } = "array"; [XmlAttribute("type")] public string csType { get; set; } = "NA"; [XmlAttribute("size")] public string csSize { get; set; } = "-1"; public XmlSchema GetSchema() => null; public void ReadXml(XmlReader reader) { // 读取节点属性 csName = reader.GetAttribute("name"); csAccess = reader.GetAttribute("access"); xsi_xmlType = reader.GetAttribute("type", XsiNs); csType = reader.GetAttribute("type"); csSize = reader.GetAttribute("size"); // 读取子Value节点 reader.ReadStartElement(); if (reader.LocalName == "Value") { var serializer = new XmlSerializer(typeof(ST_Value)); value = (ST_Value)serializer.Deserialize(reader); } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { // 写入属性,手动输出xsi:type writer.WriteAttributeString("name", csName); writer.WriteAttributeString("access", csAccess); writer.WriteAttributeString("xsi", "type", XsiNs, xsi_xmlType); writer.WriteAttributeString("type", csType); writer.WriteAttributeString("size", csSize); // 写入子Value节点 if (value != null) { var serializer = new XmlSerializer(typeof(ST_Value)); var ns = new XmlSerializerNamespaces(); ns.Add("", DefaultNs); serializer.Serialize(writer, value, ns); } } } public class ST_Field : IXmlSerializable { [XmlElement("Value", Namespace = DefaultNs)] public ST_Value csValue { get; set; } = new ST_Value(); [XmlAttribute("name")] public string csName { get; set; } = "Name of Type"; // 支持array/collection两种取值 [XmlIgnore] public string xsi_xmlType { get; set; } = "array"; [XmlAttribute("type")] public string csType { get; set; } = "NA"; [XmlAttribute("size")] public string csSize { get; set; } = "1"; public XmlSchema GetSchema() => null; public void ReadXml(XmlReader reader) { csName = reader.GetAttribute("name"); xsi_xmlType = reader.GetAttribute("type", XsiNs); csType = reader.GetAttribute("type"); csSize = reader.GetAttribute("size"); reader.ReadStartElement(); if (reader.LocalName == "Value") { var serializer = new XmlSerializer(typeof(ST_Value)); csValue = (ST_Value)serializer.Deserialize(reader); } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { writer.WriteAttributeString("name", csName); writer.WriteAttributeString("xsi", "type", XsiNs, xsi_xmlType); writer.WriteAttributeString("type", csType); writer.WriteAttributeString("size", csSize); if (csValue != null) { var serializer = new XmlSerializer(typeof(ST_Value)); var ns = new XmlSerializerNamespaces(); ns.Add("", DefaultNs); serializer.Serialize(writer, csValue, ns); } } } public class ST_Value { // Value下直接排列Field子节点,无外层包裹,用XmlElement标记 [XmlElement("Field", Namespace = DefaultNs)] public ST_Field[] csDatas { get; set; } = null; [XmlAttribute("key")] public int csKey { get; set; } = -1; [XmlAttribute("link")] public string csLink { get; set; } = "NA"; // 补全叶子节点的value属性 [XmlAttribute("value")] public string csValue { get; set; } }
第二步:修正序列化/反序列化方法
不需要手动额外处理xsi命名空间,实体类的自定义读写逻辑会自动生成符合要求的XML结构:
private ST_DtxInstance DeserializeDTX(string xmlPath) { DisplayFile(xmlPath); XmlSerializer serializer = new XmlSerializer(typeof(ST_DtxInstance)); using var fileReader = new FileStream(xmlPath, FileMode.Open); var result = (ST_DtxInstance)serializer.Deserialize(fileReader); return result; } private void SerializeDTX(ST_DtxInstance instance, string xmlPath) { XmlSerializer serializer = new XmlSerializer(typeof(ST_DtxInstance)); using var fileWriter = new StreamWriter(xmlPath); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("xsi", XsiNs); ns.Add("", DefaultNs); serializer.Serialize(fileWriter, instance, ns); fileWriter.Close(); DisplayFile(xmlPath); } private void DisplayFile(string path) { labl.Content = File.ReadAllText(path); }
兼容性说明
- 序列化时
xsi:type属性会按xsi_xmlType字段的取值正常输出,支持array、collection任意赋值 - 反序列化时会自动读取节点上的
xsi:type值到xsi_xmlType字段,不会触发内置的多态类型校验错误 - 生成的XML完全匹配目标格式,不会出现多余的命名空间声明、节点顺序错误问题,可正常识别空Field节点(无Value子节点的自闭合标签)
内容的提问来源于stack exchange,提问作者Marrt
相关产品推荐
相关产品推荐

