05 February 2017

Enum deserialization with Reflection

While you work on middleware integration applications, it quite common that the application being interacted are having different message exchange formats. Popular message exchange formats are XML & JSON.

Assume you are integrating an application which exposes data in XML format and you need send that data to another application which expects data to be sent in JSON.

So with XML de serilization you build an object and the object needs to de serialized into JSON text.

Problem statement

If your XSD has enumeration values with spaces, then upon generating the proxies, your .NET enum values will have XmlEnumAttribute applied to them.

Problem statemen example:

Let’s consider below XSD for LoanType enumeration.

<xs:element name=" LoanType" >
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Home Loan"/>
<xs:enumeration value="Student Loan"/>
<xs:enumeration value="Personal Loan"/>
<xs:enumeration value="Gold Loan"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

Then upon generating the proxy classes the LoanType enum will look like below in C# .NET.

using System.Xml.Serialization;
public enum LoanType
    {
        [XmlEnumAttribute("Home Loan")]
        HomeLoan,

        [XmlEnumAttribute("Student Loan")]
        StudentLoan,

        [XmlEnumAttribute("Personal Loan")]
        PersonalLoan,

        [XmlEnumAttribute("Gold Loan")]
        GoldLoan
    }

If you use popular JOSN Serializer Newtonsoft.Json, then you will not get the exact values as defined XmlEnumAttribute in a more readable fashion.

The JSON de serialization text for LoanType will have either integer value(0, 1, 2, 3). This might not be acceptable for the receiving system. Then you need write custom logic in getting the XmlEnumAttribute values for enum values.

Solution to the problem

We can use Reflection to solve the problem here to get the XmlEnumAttribute for enum values. Something like below in C# .NET.

public static class ReflectionHelper
    {
        public static Dictionary<stringstring>  GetXmlEnumAttributeValues(IEnumerable<Type> forTypes)
        {
            IEnumerable<Type> enumTypes = GetEnumsFieldTypes(forTypes);

            Dictionary<stringstring> enumXmlEnumAttributeValues = new Dictionary<stringstring>();

            foreach (Type enumType in enumTypes)
            {
                MemberInfo[] memberInfos = 
           enumType.GetMembers
                 (BindingFlags.Public | BindingFlags.Static);
                foreach (MemberInfo memberInfo in memberInfos)
                {
            XmlEnumAttribute xmlEnumAttribute = 
              memberInfo.GetCustomAttributes
              (typeof(XmlEnumAttribute), false).FirstOrDefault() 
              as XmlEnumAttribute;
                    if (xmlEnumAttribute != null)
                    {
                        string keyName = enumType.FullName + memberInfo.Name;
                        if (!enumXmlEnumAttributeValues.ContainsKey(keyName))
                        {
                            enumXmlEnumAttributeValues.Add(keyName, xmlEnumAttribute.Name);
                        }
                    }
                }
            }

            return enumXmlEnumAttributeValues;
        }

private static IEnumerable<Type> GetEnumsFieldTypes
            (IEnumerable<Type> types)
        {
            List<Type> allEnumtypes = new List<Type>();
            foreach (Type type in types)
            {
                PropertyInfo[] propertyInfos = type.GetProperties();

                var enumTypes = from p in propertyInfos
                                where p.PropertyType.IsEnum
                                select p.PropertyType;

                allEnumtypes.AddRange(enumTypes);
            }

            return allEnumtypes;
        }
    }
private static Dictionary<stringstring> enumValues = 
ReflectionHelper.GetXmlEnumAttributeValues(new System.Collections.Generic.List<Type>{ typeof(Loan) });

   
private static string GetEnumText(Type enumtype, string value)
        {
            string keyName = enumtype.FullName + value;

            if (enumValues.ContainsKey(keyName))
            {
                value = enumValues[keyName];
            }
            return value;
        }

Then if you want to XmlEnumAttribute for a enum value, you can pass in the class type defining the enum. In this case it could be a "LoanType" type like below:

public class Loan
    {
        public LoanType LoanType; 
    }

Then when you have Loan object you can get its LoanType enum’s XmlEnumAttribute value like below:

Loan loan = new Loan() { LoanType = LoanType.HomeLoan };

string enumText = GetEnumText(typeof(LoanType), loan. LoanType);

Then enumText would have “Home Loan” instead of default JSON de searialized values of integer values.

No comments:

Post a Comment