You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:57:13