如何为XmlElement字段定义多个名称

本文关键字:定义 字段 XmlElement | 更新日期: 2023-09-27 18:12:30

我有一个客户端应用程序提供给我的c#应用程序的XML文档。下面是客户端发送XML文件的方式:

<?xml version="1.0" encoding="utf-8"?>
<SomeAccount>
    <parentId>2380983</parentId>
    <!-- more elements -->
</SomeAccount>

和一个支持XML反序列化的c#类:

[XmlRoot]
public class SomeAccount
{
    [XmlElement("parentId")]
    public long ParentId { get; set; }
    //rest of fields...
}

但是有一些客户的系统以这种方式发送XML(注意LeParentId中的大写字母):

<?xml version="1.0" encoding="utf-8"?>
<SomeAccount>
    <LeParentId>2380983</LeParentId>
    <!-- similar for the other elements -->
</SomeAccount>

如何使这个字段(和其他字段)同时支持XML名称parentIdLeParentId ?

这是我目前用于XML反序列化的方法:

public sealed class XmlSerializationUtil
{
    public static T Deserialize<T>(string xml)
    {
        if (xml == null)
            return default(T);
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        StringReader stringReader = new StringReader(xml);
        return (T)serializer.Deserialize(stringReader);
    }
}

我试图在字段中添加[XmlElement]两次,每个元素名称一次,但这不起作用。

如何为XmlElement字段定义多个名称

例2 -让我们自己使用未知元素处理事件来实现它(尽管有一些限制,请参阅下面的注释):

public class XmlSynonymDeserializer : XmlSerializer
{
    public class SynonymsAttribute : Attribute
    {
        public readonly ISet<string> Names;
        public SynonymsAttribute(params string[] names)
        {
            this.Names = new HashSet<string>(names);
        }
        public static MemberInfo GetMember(object obj, string name)
        {
            Type type = obj.GetType();
            var result = type.GetProperty(name);
            if (result != null)
                return result;
            foreach (MemberInfo member in type.GetProperties().Cast<MemberInfo>().Union(type.GetFields()))
                foreach (var attr in member.GetCustomAttributes(typeof(SynonymsAttribute), true))
                    if (attr is SynonymsAttribute && ((SynonymsAttribute)attr).Names.Contains(name))
                        return member;
            return null;
        }
    }
    public XmlSynonymDeserializer(Type type)
        : base(type)
    {
        this.UnknownElement += this.SynonymHandler;
    }
    public XmlSynonymDeserializer(Type type, XmlRootAttribute root)
        : base(type, root)
    {
        this.UnknownElement += this.SynonymHandler;
    }
    protected void SynonymHandler(object sender, XmlElementEventArgs e)
    {
        var member = SynonymsAttribute.GetMember(e.ObjectBeingDeserialized, e.Element.Name);
        Type memberType;
        if (member != null && member is FieldInfo)
            memberType = ((FieldInfo)member).FieldType;
        else if (member != null && member is PropertyInfo)
            memberType = ((PropertyInfo)member).PropertyType;
        else
            return;
        if (member != null)
        {
            object value;
            XmlSynonymDeserializer serializer = new XmlSynonymDeserializer(memberType, new XmlRootAttribute(e.Element.Name));
            using (System.IO.StringReader reader = new System.IO.StringReader(e.Element.OuterXml))
                value = serializer.Deserialize(reader);
            if (member is FieldInfo)
                ((FieldInfo)member).SetValue(e.ObjectBeingDeserialized, value);
            else if (member is PropertyInfo)
                ((PropertyInfo)member).SetValue(e.ObjectBeingDeserialized, value);
        }
    }
}

现在类的实际代码是:

[XmlRoot]
public class SomeAccount
{
    [XmlElement("parentId")]
    [XmlSynonymDeserializer.Synonyms("LeParentId", "AnotherGreatName")]
    public long ParentId { get; set; }
    //rest of fields...
}

要反序列化,只需使用XmlSynonymDeserializer而不是常规的XmlSerializer。这应该可以满足大多数基本需求。

已知限制:

  • 此实现仅支持具有多个名称的元素;为属性扩展它应该是微不足道的
  • 支持在实体相互继承未被测试的情况下处理属性/字段
  • 此实现不检查编程错误(具有只读/常量字段/属性的属性,具有相同同义词的多个成员等)

我知道这是一个旧的帖子,但也许这将帮助其他人有同样的问题。您可以使用XmlChoiceIdentifier来解决这个问题。

[XmlRoot]
public class SomeAccount
{
    [XmlIgnore]
    public ItemChoiceType EnumType;
    [XmlChoiceIdentifier("EnumType")]
    [XmlElement("LeParentId")]
    [XmlElement("parentId")]
    public long ParentId { get; set; }
    //rest of fields...
} 
[XmlType(IncludeInSchema = false)]
public enum ItemChoiceType
{
    LeParentId,
    parentId
}

现在,如果您有一个新的xml版本和一个新的XmlElement名称,只需将该名称添加到ItemChoiceType enum中,并将一个新的XmlElement添加到属性中。

如果您只需要一个名字,这里有一个快速(但相当难看)的解决方案,在我的工作中,当我们只需要读取XML时(这对于序列化回XML会有问题),我们部署了这个解决方案,因为它是最简单和最容易理解的:

[XmlRoot]
public class SomeAccount
{
    [XmlElement("parentId")]
    public long ParentId { get; set; }
    [XmlElement("LeParentId")]
    public long LeParentId { get { return this.ParentId; } set { this.ParentId = value; } }
    //rest of fields...
}