Click here to Skip to main content
15,892,809 members
Articles / Programming Languages / XML

Yet Another XML Serialization Library for the .NET Framework

Rate me:
Please Sign up or sign in to vote.
4.92/5 (91 votes)
2 Oct 2012MIT24 min read 516.3K   207  
A flexible XML serialization library that lets developers design the XML file structure, and select the exception handling policy. YAXLib supports polymorphic serialization and serializing generic and non-generic collection classes and arrays.
// Copyright 2009 - 2010 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Collections;
using System.Reflection;

namespace YAXLib
{
    /// <summary>
    /// A utility class for reflection related stuff
    /// </summary>
    internal static class ReflectionUtils
    {
        /// <summary>
        /// Determines whether the specified type is basic type. A basic type is one that can be wholly expressed
        /// as an XML attribute. All primitive data types and type <c>string</c> and <c>DataTime</c> are basic.
        /// </summary>
        /// <param name="t">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type is a basic type; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsBasicType(Type t)
        {
            if (t == typeof(string) || t.IsPrimitive || t.IsEnum || t == typeof(DateTime))
            {
                return true;
            }
            else
            {
                Type nullableValueType;
                if(IsNullable(t, out nullableValueType))
                    return IsBasicType(nullableValueType);
                return false;
            }
        }

        /// <summary>
        /// Determines whether the specified type is array.
        /// </summary>
        /// <param name="type">The type to check</param>
        /// <param name="elementType">Type of the containing element.</param>
        /// <returns>
        /// 	<value><c>true</c> if the specified type is array; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsArray(Type type, out Type elementType)
        {
            if (type.IsArray)
            {
                elementType = type.GetElementType();
                return true;
            }
            else if (type == typeof(Array)) // i.e., a direct ref to System.Array
            {
                elementType = typeof(object);
                return true;
            }
            else
            {
                elementType = typeof(object);
                return false;
            }
        }

        /// <summary>
        /// Determines whether the specified type is array.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <c>true</c> if the specified type is array; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsArray(Type type)
        {
            Type elementType;
            return IsArray(type, out elementType);
        }

        /// <summary>
        /// Gets the array dimensions.
        /// </summary>
        /// <param name="ar">The array to return its dimensions.</param>
        /// <returns>the specified array's dimensions</returns>
        public static int[] GetArrayDimensions(object ar)
        {
            int[] dims = null;
            if (IsArray(ar.GetType()))
            {
                System.Array arObj = ar as System.Array;
                dims = new int[arObj.Rank];
                for (int i = 0; i < dims.Length; i++)
                    dims[i] = arObj.GetLength(i);
            }

            return dims;
        }

        /// <summary>
        /// Gets the friendly name for the type. Recommended for generic types.
        /// </summary>
        /// <param name="type">The type to get its friendly name</param>
        /// <returns>The friendly name for the type</returns>
        public static string GetTypeFriendlyName(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            string name = type.Name;
            if (type.IsGenericType)
            {
                int backqIndex = name.IndexOf('`');
                if (backqIndex == 0)
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Bad type name: {0}", name));
                }
                else if (backqIndex > 0)
                {
                    name = name.Substring(0, backqIndex);
                }

                name += "Of";

                Array.ForEach(
                    type.GetGenericArguments(),
                    genType => name += GetTypeFriendlyName(genType));
            }
            else if (type.IsArray)
            {
                Type t = type.GetElementType();
                name = String.Format(CultureInfo.InvariantCulture, "Array{0}Of{1}", type.GetArrayRank(), GetTypeFriendlyName(t));
            }

            return name;
        }

        /// <summary>
        /// Determines whether the type specified contains generic parameters or not.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the type contains generic parameters; otherwise,<c>false</c>.</value>
        /// </returns>
        public static bool TypeContainsGenericParameters(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (type.IsGenericType)
            {
                foreach (Type genType in type.GetGenericArguments())
                {
                    if (genType.IsGenericParameter)
                    {
                        return true;
                    }
                    else if (TypeContainsGenericParameters(genType))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is a collection type, i.e., it implements IEnumerable.
        /// Although System.String is derived from IEnumerable, it is considered as an exception.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type is a collection type; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsCollectionType(Type type)
        {
            if (type == typeof(string))
            {
                return false;
            }

            return IsIEnumerable(type);
        }

        /// <summary>
        /// Determines whether the specified type has implemented or is an <c>IEnumerable</c> or <c>IEnumerable&lt;&gt;</c>
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// 	<value><c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsIEnumerable(Type type)
        {
            Type seqType;
            return IsIEnumerable(type, out seqType);
        }

        /// <summary>
        /// Determines whether the specified type has implemented or is an <c>IEnumerable</c> or <c>IEnumerable&lt;&gt;</c> .
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="seqType">Type of the sequence items.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsIEnumerable(Type type, out Type seqType)
        {
            // detect arrays early
            if (IsArray(type, out seqType))
                return true;

            seqType = typeof(object);
            if (type == typeof(IEnumerable))
                return true;

            bool isNongenericEnumerable = false;

            if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                seqType = type.GetGenericArguments()[0];
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                {
                    Type[] genArgs = interfaceType.GetGenericArguments();
                    seqType = genArgs[0];
                    return true;
                }
                else if (interfaceType == typeof(IEnumerable))
                {
                    isNongenericEnumerable = true;
                }
            }

            // the second case is a direct reference to IEnumerable
            if (isNongenericEnumerable || type == typeof(IEnumerable))
            {
                seqType = typeof(object);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Gets the type of the items of a collection type.
        /// </summary>
        /// <param name="type">The type of the collection.</param>
        /// <returns>The type of the items of a collection type.</returns>
        public static Type GetCollectionItemType(Type type)
        {
            Type itemType = typeof(object);
            if (IsIEnumerable(type, out itemType))
                return itemType;
            else
                throw new Exception("The specified type must be a collection");
        }

        /// <summary>
        /// Determines whether the specified type has implemented <c>IList</c>.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type has implemented <c>IList</c>; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsIList(Type type)
        {
            // a direct reference to the interface itself is also OK.
            if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IList<>))
            {
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type has implemented the <c>ICollection</c> interface.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="itemType">Type of the member items.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type has implemented the <c>ICollection</c> interface; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsICollection(Type type, out Type itemType)
        {
            itemType = typeof(object);

            // a direct reference to the interface itself is also OK.
            if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                itemType = type.GetGenericArguments()[0];
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
                {
                    itemType = interfaceType.GetGenericArguments()[0];
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is a generic dictionary.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="keyType">Type of the key.</param>
        /// <param name="valueType">Type of the value.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type has implemented the IDictionary interface; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsIDictionary(Type type, out Type keyType, out Type valueType)
        {
            keyType = typeof(object);
            valueType = typeof(object);

            // a direct reference to the interface itself is also OK.
            if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
            {
                Type[] genArgs = type.GetGenericArguments();
                keyType = genArgs[0];
                valueType = genArgs[1];
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                {
                    Type[] genArgs = interfaceType.GetGenericArguments();
                    keyType = genArgs[0];
                    valueType = genArgs[1];
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is a generic dictionary.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type is dictionary; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsIDictionary(Type type)
        {
            Type keyType, valueType;
            return IsIDictionary(type, out keyType, out valueType);
        }

        /// <summary>
        /// Determines whether the specified type is a non generic IDictionary, e.g., a Hashtable.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is a non generic IDictionary; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsNonGenericIDictionary(Type type)
        {
            // a direct reference to the interface itself is also OK.
            if (type == typeof(IDictionary))
            {
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType == typeof(IDictionary))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is equal or inherited from another specified type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="baseType">Another type that the specified type is checked whether it is equal or
        /// has been driven from.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type is equal or inherited from another specified type; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsTypeEqualOrInheritedFromType(Type type, Type baseType)
        {
            if (type.IsGenericType)
                type = type.GetGenericTypeDefinition();

            if (type == baseType)
                return true;

            Type curBaseType = type.BaseType;
            while (curBaseType != null)
            {
                if (curBaseType == baseType)
                    return true;

                curBaseType = curBaseType.BaseType;
            }

            return false;
        }

        /// <summary>
        /// Determines whether the specified type is nullable.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="valueType">The value type of the corresponding nullable type.</param>
        /// <returns>
        /// <c>true</c> if the specified type is nullable; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsNullable(Type type, out Type valueType)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                valueType = type.GetGenericArguments()[0];
                return true;
            }

            valueType = null;
            return false;
        }

        /// <summary>
        /// Determines whether the specified type is nullable.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <c>true</c> if the specified type is nullable; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsNullable(Type type)
        {
            Type valueType;
            return IsNullable(type, out valueType);
        }

        /// <summary>
        /// Determines whether the specified type implements <c>IFormattable</c>
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// 	<c>true</c> if the specified type implements <c>IFormattable</c>; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsIFormattable(Type type)
        {
            // a direct reference to the interface itself is also OK.
            if (type == typeof(IFormattable))
            {
                return true;
            }

            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType == typeof(IFormattable))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines whether the type provides the functionality 
        /// to format the value of an object into a string representation.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <value><c>true</c> if the specified type impliments the <c>IFormattable</c> interface; otherwise, <c>false</c>.</value>
        /// </returns>
        public static bool IsStringConvertibleIFormattable(Type type)
        {
            // is IFormattable
            // accept parameterless ToString
            // accept ctor of string
            if (IsIFormattable(type) && !HasOneReadWriteProperty(type))
            {
                if (null != type.GetConstructor(new Type[] { typeof(string) }))
                {
                    if (null != type.GetMethod("ToString", new Type[0]) &&
                        null != type.GetMethod("ToString", new Type[] { typeof(string) })) 
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Checks to see if the specified type has readable and writable properties.
        /// </summary>
        /// <param name="type">The type to check for.</param>
        /// <returns><value><c>true</c> if the specified type has readable and writable properties; otherwise, <c>false</c>.</value></returns>
        public static bool HasOneReadWriteProperty(Type type)
        {
            PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo pi in props)
            {
                if (pi.CanRead && pi.CanWrite)
                {
                    MethodInfo getPi = pi.GetGetMethod(false);
                    MethodInfo setPi = pi.GetSetMethod(false);
                    if (setPi != null && getPi != null)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Tries to format the specified object using the format string provided.
        /// If the formatting operation is not applicable, the source object is returned intact.
        /// Note: The type of the returned object will be 'System.String' if formatting succeeds.
        /// </summary>
        /// <param name="src">The source object.</param>
        /// <param name="format">The format string.</param>
        /// <returns><code>System.String</code> if the format is successful; otherwise, the original object</returns>
        public static object TryFormatObject(object src, string format)
        {
            if (format == null || src == null)
            {
                return src;
            }

            object formattedObject = null;

            try
            {
                formattedObject = src.GetType().InvokeMember("ToString", BindingFlags.InvokeMethod, null, src, new object[] { format });
            }
            catch
            {
                return src;
                //throw new YAXInvalidFormatProvided(src.GetType(), format);
                //this.OnExceptionOccurred(new YAXInvalidFormatProvided(src.GetType(), format), this.m_defaultExceptionType);
            }

            return formattedObject ?? src;
        }

        /// <summary>
        /// Converts the specified object from a basic type to another type as specified.
        /// It is meant by basic types, primitive data types, strings, and enums.
        /// </summary>
        /// <param name="value">The object to be converted.</param>
        /// <param name="dstType">the destination type of conversion.</param>
        /// <returns>the converted object</returns>
        public static object ConvertBasicType(object value, Type dstType)
        {
            //if (!ReflectionUtils.IsBasicType(dstType))
            //{
            //    throw new ArgumentException("Destination type must be a basic type", "dstType");
            //}

            object convertedObj = null;
            if (dstType.IsEnum)
            {
                UdtWrapper typeWrapper = TypeWrappersPool.Pool.GetTypeWrapper(dstType, null);
                convertedObj = typeWrapper.EnumWrapper.ParseAlias(value.ToString());
            }
            else if (dstType == typeof(bool))
            {
                string strValue = value.ToString().Trim().ToLower();
                if (strValue == "false" || strValue == "no" || strValue == "0")
                    convertedObj = false;
                else if (strValue == "true" || strValue == "yes" || strValue == "1")
                    convertedObj = true;
                else
                {
                    int boolIntValue = 0;
                    if (Int32.TryParse(strValue, out boolIntValue))
                        convertedObj = boolIntValue == 0 ? false : true;
                    else
                        throw new Exception("The specified value is not recognized as boolean: " + strValue);
                }
            }
            else
            {
                Type nullableType;
                if (IsNullable(dstType, out nullableType))
                {
                    return ConvertBasicType(value, nullableType);
                }

                convertedObj = Convert.ChangeType(value, dstType);
            }

            return convertedObj;
        }

        /// <summary>
        /// Searches all loaded assemblies to find a type with a special name.
        /// </summary>
        /// <param name="name">The name of the type to find.</param>
        /// <returns>type found using the specified name</returns>
        public static Type GetTypeByName(string name)
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

            // first search the 1st assembly (i.e. the mscorlib), then start from the last assembly backward, 
            // the last assemblies are user defined ones
            for(int i = assemblies.Length; i > 0; i--)
            {
                Assembly curAssembly = (i == assemblies.Length) ? assemblies[0] : assemblies[i];

                try
                {
                    Type type = curAssembly.GetType(name, false, true);
                    if (type != null)
                        return type;
                }
                catch
                {
                }
            }

            return null;
        }

        /// <summary>
        /// Determines whether the specified property is public.
        /// </summary>
        /// <param name="pi">The property.</param>
        /// <returns>
        /// <c>true</c> if the specified property is public; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsPublicProperty(PropertyInfo pi)
        {
            foreach(var m in pi.GetAccessors())
                if(m.IsPublic) 
                    return true;

            return false;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer
Australia Australia
A software designer and developer

Comments and Discussions