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

.NET 4.6.1 Xml反序列化:根据international属性映射类属性

解决XML反序列化中根据元素属性值映射到不同类属性的问题

针对你遇到的这个需求——把带有international属性的重复XML元素映射到类的不同属性上,.NET的XmlSerializer本身没有直接提供这种“根据属性值路由”的特性,但我们有两种实用的实现方式,下面分别详细说明:

方法一:中间集合+只读属性(简单易上手)

这种思路是先把所有同名的AccountNumber和BankCode元素收集到集合里,再通过标记[XmlIgnore]的只读属性,根据international属性值筛选出对应的值,赋值给你需要的类属性。

1. 定义元素承载类

首先创建两个小类,分别用来捕获AccountNumber和BankCode元素的文本内容以及它们的international属性:

public class AccountNumberElement
{
    // 捕获元素的文本内容
    [XmlText]
    public string Value { get; set; }

    // 捕获international属性
    [XmlAttribute("international")]
    public bool IsInternational { get; set; }
}

public class BankCodeElement
{
    [XmlText]
    public string Value { get; set; }

    [XmlAttribute("international")]
    public bool IsInternational { get; set; }
}

这里要特别提醒:你的原始类里AccountNumberInternational定义为long类型,但XML里的国际账户号是DE10000(包含字母),直接用long会反序列化失败,所以我把AccountNumberElement的Value改成了string,后续可以根据需求转换类型。

2. 修改AccountInfo类

在AccountInfo中定义集合属性来接收所有同名元素,再通过只读属性提取对应值:

[XmlRoot("AccountInfo")]
public class AccountInfo
{
    [XmlAttribute]
    public int ID { get; set; }

    [XmlAttribute]
    public string Currency { get; set; }

    // 捕获所有AccountNumber元素
    [XmlElement("AccountNumber")]
    public List<AccountNumberElement> AccountNumbers { get; set; } = new List<AccountNumberElement>();

    // 捕获所有BankCode元素
    [XmlElement("BankCode")]
    public List<BankCodeElement> BankCodes { get; set; } = new List<BankCodeElement>();

    public string AccountHolder { get; set; }

    // 非国际账户号(对应international=false)
    [XmlIgnore]
    public long AccountNumber 
    { 
        get 
        {
            var element = AccountNumbers.FirstOrDefault(a => !a.IsInternational);
            return element != null ? long.Parse(element.Value) : 0;
        }
        set 
        {
            var element = AccountNumbers.FirstOrDefault(a => !a.IsInternational);
            if (element == null)
            {
                element = new AccountNumberElement { IsInternational = false };
                AccountNumbers.Add(element);
            }
            element.Value = value.ToString();
        }
    }

    // 国际账户号(对应international=true)
    [XmlIgnore]
    public string AccountNumberInternational
    {
        get 
        {
            var element = AccountNumbers.FirstOrDefault(a => a.IsInternational);
            return element?.Value ?? string.Empty;
        }
        set
        {
            var element = AccountNumbers.FirstOrDefault(a => a.IsInternational);
            if (element == null)
            {
                element = new AccountNumberElement { IsInternational = true };
                AccountNumbers.Add(element);
            }
            element.Value = value;
        }
    }

    // 非国际银行代码
    [XmlIgnore]
    public string BankCode
    {
        get 
        {
            var element = BankCodes.FirstOrDefault(b => !b.IsInternational);
            return element?.Value ?? string.Empty;
        }
        set
        {
            var element = BankCodes.FirstOrDefault(b => !b.IsInternational);
            if (element == null)
            {
                element = new BankCodeElement { IsInternational = false };
                BankCodes.Add(element);
            }
            element.Value = value;
        }
    }

    // 国际银行代码
    [XmlIgnore]
    public string BankCodeInternational
    {
        get 
        {
            var element = BankCodes.FirstOrDefault(b => b.IsInternational);
            return element?.Value ?? string.Empty;
        }
        set
        {
            var element = BankCodes.FirstOrDefault(b => b.IsInternational);
            if (element == null)
            {
                element = new BankCodeElement { IsInternational = true };
                BankCodes.Add(element);
            }
            element.Value = value;
        }
    }
}

使用示例

反序列化代码和常规用法完全一致:

var xml = @"<AccountInfo ID=""accid1"" Currency=""EUR""> 
<AccountNumber international=""false"">10000</AccountNumber> 
<AccountNumber international=""true"">DE10000</AccountNumber> 
<BankCode international=""false"">22222</BankCode> 
<BankCode international=""true"">BC22222</BankCode> 
<AccountHolder>Some Dude</AccountHolder> 
</AccountInfo>";

var serializer = new XmlSerializer(typeof(AccountInfo));
using var reader = new StringReader(xml);
var accountInfo = (AccountInfo)serializer.Deserialize(reader);

// 访问属性
Console.WriteLine(accountInfo.AccountNumber); // 输出10000
Console.WriteLine(accountInfo.AccountNumberInternational); // 输出DE10000

方法二:实现IXmlSerializable接口(精确控制)

如果需要完全掌控反序列化的每一步,或者不想引入中间集合,可以实现IXmlSerializable接口,手动读取XML节点并赋值到对应的类属性上:

[XmlRoot("AccountInfo")]
public class AccountInfo : IXmlSerializable
{
    public int ID { get; set; }
    public string Currency { get; set; }
    public long AccountNumber { get; set; } // international=false
    public string BankCode { get; set; }
    public string AccountNumberInternational { get; set; } // international=true
    public string BankCodeInternational { get; set; }
    public string AccountHolder { get; set; }

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        // 读取根节点的属性
        ID = int.Parse(reader.GetAttribute("ID"));
        Currency = reader.GetAttribute("Currency");

        // 移动到第一个子节点
        reader.ReadStartElement();

        while (!reader.EOF)
        {
            if (reader.IsStartElement())
            {
                switch (reader.LocalName)
                {
                    case "AccountNumber":
                        bool isInternational = bool.Parse(reader.GetAttribute("international"));
                        string accountNumValue = reader.ReadElementContentAsString();
                        if (isInternational)
                        {
                            AccountNumberInternational = accountNumValue;
                        }
                        else
                        {
                            AccountNumber = long.Parse(accountNumValue);
                        }
                        break;
                    case "BankCode":
                        isInternational = bool.Parse(reader.GetAttribute("international"));
                        string bankCodeValue = reader.ReadElementContentAsString();
                        if (isInternational)
                        {
                            BankCodeInternational = bankCodeValue;
                        }
                        else
                        {
                            BankCode = bankCodeValue;
                        }
                        break;
                    case "AccountHolder":
                        AccountHolder = reader.ReadElementContentAsString();
                        break;
                    default:
                        // 跳过未知节点,避免反序列化失败
                        reader.Skip();
                        break;
                }
            }
            else
            {
                // 移动到下一个节点
                reader.Read();
            }
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        // 写入根节点属性
        writer.WriteAttributeString("ID", ID.ToString());
        writer.WriteAttributeString("Currency", Currency);

        // 写入非国际账户号
        writer.WriteStartElement("AccountNumber");
        writer.WriteAttributeString("international", "false");
        writer.WriteString(AccountNumber.ToString());
        writer.WriteEndElement();

        // 写入国际账户号
        writer.WriteStartElement("AccountNumber");
        writer.WriteAttributeString("international", "true");
        writer.WriteString(AccountNumberInternational);
        writer.WriteEndElement();

        // 写入非国际银行代码
        writer.WriteStartElement("BankCode");
        writer.WriteAttributeString("international", "false");
        writer.WriteString(BankCode);
        writer.WriteEndElement();

        // 写入国际银行代码
        writer.WriteStartElement("BankCode");
        writer.WriteAttributeString("international", "true");
        writer.WriteString(BankCodeInternational);
        writer.WriteEndElement();

        // 写入账户持有人
        writer.WriteElementString("AccountHolder", AccountHolder);
    }
}

这种方法的优势是完全自定义读写逻辑,适合复杂的XML结构,但需要自己处理所有节点的解析和生成,代码量会多一些。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:35:14