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

C#根据XML节点id属性反序列化不同类型data节点的方案

核心原因

XmlSerializer 默认不会根据XML属性值自动匹配派生类型,它识别派生类的唯一标识是节点上的xsi:type属性。你的XML中没有该标识,因此所有row节点都会被反序列化为基类row的实例,派生类定义的属性自然无法被赋值,出现数据丢失。
xsd工具生成合并属性的通用类,是因为XSD语法本身无法描述「父节点id属性值决定子节点结构」这类关联约束,只能将所有可能出现的属性合并到同一个类中。

实现方案

提供两种可直接运行的实现方式,可根据场景选择:

方案1:自定义反序列化逻辑,直接得到目标类结构

通过实现IXmlSerializable接口自定义data节点的解析规则,完全匹配期望的类结构,不需要反序列化后做二次转换。
类定义代码如下:

using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

public class document : IXmlSerializable
{
    public UserInfoRow[] dataUserInfos { get; set; }
    public UserTransactionRow[] dataUserTransactions { get; set; }

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        var infoRows = new List<UserInfoRow>();
        var transRows = new List<UserTransactionRow>();

        reader.ReadStartElement("document");
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.LocalName != "data")
            {
                reader.Skip();
                continue;
            }

            string dataType = reader.GetAttribute("id");
            reader.ReadStartElement("data");

            if (reader.LocalName == "rows")
            {
                reader.ReadStartElement("rows");
                while (reader.LocalName == "row")
                {
                    using var rowReader = reader.ReadSubtree();
                    switch (dataType)
                    {
                        case "UserInfo":
                            var infoSerializer = new XmlSerializer(typeof(UserInfoRow));
                            infoRows.Add((UserInfoRow)infoSerializer.Deserialize(rowReader));
                            break;
                        case "UserTransactions":
                            var transSerializer = new XmlSerializer(typeof(UserTransactionRow));
                            transRows.Add((UserTransactionRow)transSerializer.Deserialize(rowReader));
                            break;
                        default:
                            reader.Skip();
                            break;
                    }
                    reader.Read();
                }
                reader.ReadEndElement();
            }

            reader.ReadEndElement();
        }
        reader.ReadEndElement();

        dataUserInfos = infoRows.ToArray();
        dataUserTransactions = transRows.ToArray();
    }

    public void WriteXml(XmlWriter writer)
    {
        // 序列化逻辑可按需实现
        writer.WriteStartElement("data");
        writer.WriteAttributeString("id", "UserInfo");
        writer.WriteStartElement("rows");
        var infoSerializer = new XmlSerializer(typeof(UserInfoRow));
        foreach (var row in dataUserInfos ?? Array.Empty<UserInfoRow>())
        {
            infoSerializer.Serialize(writer, row);
        }
        writer.WriteEndElement();
        writer.WriteEndElement();

        writer.WriteStartElement("data");
        writer.WriteAttributeString("id", "UserTransactions");
        writer.WriteStartElement("rows");
        var transSerializer = new XmlSerializer(typeof(UserTransactionRow));
        foreach (var row in dataUserTransactions ?? Array.Empty<UserTransactionRow>())
        {
            transSerializer.Serialize(writer, row);
        }
        writer.WriteEndElement();
        writer.WriteEndElement();
    }
}

public class UserInfoRow
{
    [XmlAttribute("id")]
    public string id { get; set; }
    [XmlAttribute("Name")]
    public string Name { get; set; }
    [XmlAttribute("phone")]
    public string phone { get; set; }
}

public class UserTransactionRow
{
    [XmlAttribute("UserId")]
    public string UserId { get; set; }
    [XmlAttribute("amount")]
    public decimal amount { get; set; }
    [XmlAttribute("date")]
    public DateTime date { get; set; }
}

调用方式和标准XmlSerializer完全一致:

using var stream = File.OpenRead("target.xml");
var serializer = new XmlSerializer(typeof(document));
var result = (document)serializer.Deserialize(stream);

方案2:保留中间类结构,反序列化后手动映射

如果不想自定义序列化接口,可以保留通用中间类做自动反序列化,完成后根据data节点的id属性,手动将行数据映射为对应强类型。
注意不要使用基类+XmlInclude的方案,该方案依赖XML中的xsi:type属性,在当前场景下无法生效。可以将行节点先反序列化为XmlElement或者通用属性字典,再做映射。

注意:XmlArrayItem特性仅支持根据XML元素名区分派生类型,无法根据父节点的属性值做类型判断,不要在该场景下调试该配置。


内容的提问来源于stack exchange,提问作者Actimele

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:00:57