How to define multiple names for XmlElement field?

后端 未结 3 2061
有刺的猬
有刺的猬 2020-11-29 10:08

I have a XML document provided by client applications to my C# application. This is how a client sends the XML file:

         


        
相关标签:
3条回答
  • 2020-11-29 10:45

    If you need only one more name, here is a quick (and rather ugly) solution that we deployed in several cases in my work when we had only to read XMLs (this will be problematic for serializing back to an XML), because it's the simplest and easiest to understand:

    [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...
    }
    
    0 讨论(0)
  • I know this is an old post, but maybe this will help anyone else having the same problem. What you could use for this problem is 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
    }
    

    Now if you have a new xml version and a new XmlElement name you just add that name to the ItemChoiceType enum and a new XmlElement to the property.

    0 讨论(0)
  • 2020-11-29 10:51

    Take 2 - let's implement this ourselves using the unknown element handling event (see the comments below for some limitations though):

    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);
            }
        }
    }
    

    And now the actual code of the class would be:

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

    To deserialize, simply use XmlSynonymDeserializer instead of the regular XmlSerializer. This should work for most of the basic needs.

    Known limitations:

    • This implementation supports only elements with multiple names; extending it for attributes should be trivial
    • Support for handling of properties/fields in cases where the entities inherit from one another is not tested
    • This implementation does not check for programming bugs (having the attribute on read-only/constant field/properties, multiple members with the same synonyms and so on)
    0 讨论(0)
提交回复
热议问题