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

如何在C#中用XmlSerializer反序列化同名不同类型的XML元素

嘿,这个场景我之前做项目时遇到过,其实核心是把那些带编号的MealPeriodN和MealPeriodNSales配对成逻辑上的对象,咱们分两种情况来解决:

情况1:时段数量固定(比如已知最多3个)

如果你的XML里的时段数量是固定的(比如永远只有1-3个),可以直接通过XmlElement特性来映射每个元素,再做一个只读属性把它们分组:

首先定义一个用来承载单时段数据的类:

public class MealPeriodSales
{
    public string Name { get; set; }
    public decimal Amount { get; set; }
}

然后定义SalesSummary类,用XmlElement指定每个XML元素的名称:

[XmlRoot("SalesSummary")]
public class SalesSummary
{
    [XmlElement("MealPeriod1")]
    public string MealPeriod1Name { get; set; }

    [XmlElement("MealPeriod1Sales")]
    public decimal MealPeriod1Amount { get; set; }

    [XmlElement("MealPeriod2")]
    public string MealPeriod2Name { get; set; }

    [XmlElement("MealPeriod2Sales")]
    public decimal MealPeriod2Amount { get; set; }

    [XmlElement("MealPeriod3")]
    public string MealPeriod3Name { get; set; }

    [XmlElement("MealPeriod3Sales")]
    public decimal MealPeriod3Amount { get; set; }

    // 只读属性,把分散的字段整合成列表,用[XmlIgnore]避免序列化时被处理
    [XmlIgnore]
    public List<MealPeriodSales> MealPeriods
    {
        get
        {
            var periods = new List<MealPeriodSales>();
            if (!string.IsNullOrEmpty(MealPeriod1Name))
                periods.Add(new MealPeriodSales { Name = MealPeriod1Name, Amount = MealPeriod1Amount });
            if (!string.IsNullOrEmpty(MealPeriod2Name))
                periods.Add(new MealPeriodSales { Name = MealPeriod2Name, Amount = MealPeriod2Amount });
            if (!string.IsNullOrEmpty(MealPeriod3Name))
                periods.Add(new MealPeriodSales { Name = MealPeriod3Name, Amount = MealPeriod3Amount });
            return periods;
        }
    }
}

最后反序列化的代码:

// 假设xmlContent是你的XML字符串
using (var reader = new StringReader(xmlContent))
{
    var serializer = new XmlSerializer(typeof(SalesSummary));
    var summary = (SalesSummary)serializer.Deserialize(reader);
    // 这里summary.MealPeriods就拿到了所有配对好的时段数据
}

情况2:时段数量不固定(可能有N个)

如果XML里的时段数量是动态的(比如可能有MealPeriod4、MealPeriod5...),那上面的方法就不适用了,这时候可以通过实现IXmlSerializable接口来自定义序列化逻辑:

首先还是定义MealPeriodSales类,然后让SalesSummary实现IXmlSerializable:

using System.Globalization;
using System.Xml;
using System.Xml.Serialization;

[XmlRoot("SalesSummary")]
public class SalesSummary : IXmlSerializable
{
    public List<MealPeriodSales> MealPeriods { get; set; } = new List<MealPeriodSales>();

    // 这个方法返回null即可,不需要自定义Schema
    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        // 先读取根节点
        reader.ReadStartElement("SalesSummary");

        // 遍历所有子节点
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            // 判断当前节点是时段名称(不带Sales后缀)
            if (reader.Name.StartsWith("MealPeriod") && !reader.Name.EndsWith("Sales"))
            {
                // 读取时段名称
                var periodName = reader.ReadElementContentAsString();
                // 接下来应该是对应的Sales节点,检查节点名称是否匹配
                var expectedSalesNodeName = $"{reader.Name.Substring(0, reader.Name.LastIndexOf('d') + 1)}Sales";
                if (reader.Name == expectedSalesNodeName)
                {
                    // 读取销售额,用InvariantCulture避免区域格式问题
                    var salesAmount = decimal.Parse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture);
                    MealPeriods.Add(new MealPeriodSales { Name = periodName, Amount = salesAmount });
                }
                else
                {
                    // 如果没有对应的Sales节点,跳过当前节点
                    reader.Skip();
                }
            }
            else
            {
                // 跳过非目标节点
                reader.Skip();
            }
        }

        // 读取根节点的结束标签
        reader.ReadEndElement();
    }

    // 如果需要把对象序列化回XML,可以实现这个方法
    public void WriteXml(XmlWriter writer)
    {
        for (int i = 0; i < MealPeriods.Count; i++)
        {
            var period = MealPeriods[i];
            writer.WriteElementString($"MealPeriod{i+1}", period.Name);
            writer.WriteElementString($"MealPeriod{i+1}Sales", period.Amount.ToString("F2", CultureInfo.InvariantCulture));
        }
    }
}

public class MealPeriodSales
{
    public string Name { get; set; }
    public decimal Amount { get; set; }
}

反序列化代码和之前一样,这样不管XML里有多少个时段,都能自动配对并加载到MealPeriods列表里。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:37:45