Click here to Skip to main content
15,896,269 members
Articles / Programming Languages / Markdown

APJSON

Rate me:
Please Sign up or sign in to vote.
4.67/5 (5 votes)
28 Aug 2013CPOL13 min read 41.8K   1.2K   34  
This is an alternative for "fastJSON"
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace Apolyton.FastJson.Registry
{
    /// <summary>
    /// Represents the registry which holds property information being relevant for serialization/ deserialization. 
    /// </summary>
    /// <remarks>
    /// http://www.codeproject.com/Articles/159450/fastJSON
    /// The version over there (2.0.9)could not be taken directly, as its serializer is taking all public properties, disregarding any attribute policy. This is
    /// not good for our case, as we want to return (portions of) data objects as well.
    /// </remarks>
    public sealed class JsonRegistry
    {
        private readonly JsonParameters _owner;

        private readonly RegistrySection<Type, string> _typeNameCache;
        private readonly RegistrySection<string, Type> _typeCache;
        private readonly RegistrySection<Type, JsonPropertyInfo> _typeInfo;
        private readonly RegistrySection<Type, CreateObjectHandler> _constructorCache;
        private readonly RegistrySection<Type, IEnumerable<GetterDescriptor>> _gettersCache;
        private readonly RegistrySection<string, RegistrySection<string, JsonPropertyInfo>> _propertycache;

        internal readonly RegistrySection<Type, SerializationHandler> _customSerializer;
        internal readonly RegistrySection<Type, DeserializationHandler> _customDeserializer;

        /// <summary>
        /// Creates an instance of the registry
        /// </summary>
        internal JsonRegistry(JsonParameters owner)
        {
            _owner = owner;
            _typeNameCache = new RegistrySection<Type, string>();
            _typeInfo = new RegistrySection<Type, JsonPropertyInfo>();
            _typeCache = new RegistrySection<string, Type>();
            _constructorCache = new RegistrySection<Type, CreateObjectHandler>();
            _gettersCache = new RegistrySection<Type, IEnumerable<GetterDescriptor>>();
            _customSerializer = new RegistrySection<Type, SerializationHandler>();
            _customDeserializer = new RegistrySection<Type, DeserializationHandler>();
            _propertycache = new RegistrySection<string, RegistrySection<string, JsonPropertyInfo>>();
        }

        /// <summary>
        /// Resets all cached values.
        /// </summary>
        /// <remarks>
        /// Primarily used for unit tests where overlapping cached values 
        /// </remarks>
        internal void Reset()
        {
            _typeNameCache.Clear();
            _typeInfo.Clear();
            _typeCache.Clear();
            _constructorCache.Clear();
            _gettersCache.Clear();
            _customSerializer.Clear();
            _customDeserializer.Clear();
            _propertycache.Clear();
        }

        #region Custom Type Support

        /// <summary>
        /// Registers a custom type.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="serializer"></param>
        /// <param name="deserializer"></param>
        public void RegisterCustomType(Type type, SerializationHandler serializer, DeserializationHandler deserializer)
        {
            _customSerializer.Add(type, serializer);
            _customDeserializer.Add(type, deserializer);

            // reset property cache
            _propertycache.Clear();
        }

        /// <summary>
        /// Returns true, if the custom type is registered.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        internal bool IsCustomTypeRegistered(Type t)
        {
            SerializationHandler s;

            return _customSerializer.TryGetValue(t, out s);
        }

        #endregion

        /// <summary>
        /// Returns the assembly name.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        internal string GetTypeAssemblyName(Type t)
        {
            string val = "";

            if (_typeNameCache.TryGetValue(t, out val))
            {
                return val;
            }
            else
            {
                string s = t.AssemblyQualifiedName;
                _typeNameCache.Add(t, s);

                return s;
            }
        }

        /// <summary>
        /// Returns the type for the given type name.
        /// </summary>
        /// <param name="typename"></param>
        /// <returns></returns>
        internal Type GetTypeByName(string typename)
        {
            Type val = null;
            if (_typeCache.TryGetValue(typename, out val))
            {
                return val;
            }
            else
            {
                Type t = Type.GetType(typename);
                _typeCache.Add(typename, t);
                return t;
            }
        }

        #region Factory Methods

        /// <summary>
        /// Returns the object for the given type.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        internal object CreateInstanceFast(Type t)
        {
            try
            {
                CreateObjectHandler constructorHandler = null;

                if (_constructorCache.TryGetValue(t, out constructorHandler))
                {
                    return constructorHandler();
                }
                else
                {
                    if (t.IsAbstract)
                    {
                        if (!t.IsGenericType && t == typeof(IEnumerable))
                        {
                            return new ArrayList();
                        }
                        else
                        {
                            throw new SerializationException("Cannot create instances of abstract types " + t);
                        }
                    }
                    else if (t.IsArray)
                    {
                        throw new InvalidOperationException("Cannot create constructors for arrays. Size must be known in advance.");
                    }

                    if (t.IsClass)
                    {
                        DynamicMethod dynMethod = new DynamicMethod("_", t, null);
                        ILGenerator ilGen = dynMethod.GetILGenerator();
                        ilGen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
                        ilGen.Emit(OpCodes.Ret);

                        constructorHandler = (CreateObjectHandler)dynMethod.CreateDelegate(typeof(CreateObjectHandler));

                        _constructorCache.Add(t, constructorHandler);
                    }
                    else // structs
                    {
                        DynamicMethod dynMethod = new DynamicMethod("_",
                            MethodAttributes.Public | MethodAttributes.Static,
                            CallingConventions.Standard,
                            typeof(object),
                            null,
                            t, false);

                        ILGenerator ilGen = dynMethod.GetILGenerator();

                        var lv = ilGen.DeclareLocal(t);
                        ilGen.Emit(OpCodes.Ldloca_S, lv);
                        ilGen.Emit(OpCodes.Initobj, t);
                        ilGen.Emit(OpCodes.Ldloc_0);
                        ilGen.Emit(OpCodes.Box, t);
                        ilGen.Emit(OpCodes.Ret);
                        constructorHandler = (CreateObjectHandler)dynMethod.CreateDelegate(typeof(CreateObjectHandler));

                        _constructorCache.Add(t, constructorHandler);
                    }

                    return constructorHandler();
                }
            }
            catch (Exception ex)
            {
                throw new SerializationException(string.Format("Failed to fast create instance for type '{0}' from assembly '{1}'",
                    t.FullName, t.AssemblyQualifiedName), ex);
            }
        }

        private JsonPropertyInfo CreateJsonPropertyInfo(Type t)
        {
            JsonPropertyInfo info = new JsonPropertyInfo();
            info.CanWrite = true;
            info.PropertyOrFieldType = t;
            info.IsDictionary = typeof(IDictionary).IsAssignableFrom(t);
            info.IsValueType = t.IsValueType;
            info.IsGenericType = t.IsGenericType;
            info.IsArray = t.IsArray;

            if (info.IsArray)
            {
                info.GenericItemType = t.GetElementType();
            }

            if (info.IsGenericType)
            {
                info.GenericTypes = t.GetGenericArguments();
                info.GenericItemType = info.GenericTypes[0];
            }

            info.IsByteArray = t == typeof(byte[]);
            info.IsGuid = (t == typeof(Guid) || t == typeof(Guid?));
            info.IsHashtable = t == typeof(Hashtable);
            info.IsDataSet = t == typeof(DataSet);
            info.IsDataTable = t == typeof(DataTable);

            info.NullableType = GetNullableType(t);
            info.IsEnum = t.IsEnum;
            info.IsDateTime = t == typeof(DateTime) || t == typeof(DateTime?);
            info.IsInt = t == typeof(int) || t == typeof(int?);
            info.IsLong = t == typeof(long) || t == typeof(long?);
            info.IsString = t == typeof(string);
            info.IsBool = t == typeof(bool) || t == typeof(bool?);
            info.IsClass = t.IsClass;

            if (info.IsDictionary && info.GenericTypes != null && info.GenericTypes.Length > 0 && info.GenericTypes[0] == typeof(string))
            {
                info.IsStringDictionary = true;
            }

            if (t.IsValueType)
            {
                info.DefaultValue = Activator.CreateInstance(t);
            }

            if (IsCustomTypeRegistered(t))
            {
                info.IsCustomType = true;
            }
            return info;
        }

        private Type GetNullableType(Type targetType)
        {
            if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                return targetType.GetGenericArguments()[0];
            }

            return targetType;
        }

        /// <summary>
        /// Creates a generic setter for the given field.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldInfo"></param>
        /// <returns></returns>
        private static GenericSetterHandler CreateSetField(Type type, FieldInfo fieldInfo)
        {
            Type[] arguments = new Type[2];
            arguments[0] = arguments[1] = typeof(object);

            DynamicMethod dynamicSet = new DynamicMethod("_", typeof(object), arguments, type, true);
            ILGenerator il = dynamicSet.GetILGenerator();

            if (!type.IsClass) // structs
            {
                var lv = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Unbox_Any, type);
                il.Emit(OpCodes.Stloc_0);
                il.Emit(OpCodes.Ldloca_S, lv);
                il.Emit(OpCodes.Ldarg_1);
                if (fieldInfo.FieldType.IsClass)
                    il.Emit(OpCodes.Castclass, fieldInfo.FieldType);
                else
                    il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
                il.Emit(OpCodes.Stfld, fieldInfo);
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Box, type);
                il.Emit(OpCodes.Ret);
            }
            else
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_1);
                if (fieldInfo.FieldType.IsValueType)
                    il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
                il.Emit(OpCodes.Stfld, fieldInfo);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ret);
            }
            return (GenericSetterHandler)dynamicSet.CreateDelegate(typeof(GenericSetterHandler));
        }

        /// <summary>
        /// Creates a generic setter for the given property.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        private static GenericSetterHandler CreateSetMethod(Type type, PropertyInfo propertyInfo)
        {
            MethodInfo setMethod = propertyInfo.GetSetMethod();
            if (setMethod == null)
            {
                return null;
            }

            Type[] arguments = new Type[2];
            arguments[0] = arguments[1] = typeof(object);

            DynamicMethod setter = new DynamicMethod("_", typeof(object), arguments);
            ILGenerator il = setter.GetILGenerator();

            if (!type.IsClass) // structs
            {
                var lv = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Unbox_Any, type);
                il.Emit(OpCodes.Stloc_0);
                il.Emit(OpCodes.Ldloca_S, lv);
                il.Emit(OpCodes.Ldarg_1);

                if (propertyInfo.PropertyType.IsClass)
                {
                    il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
                }
                else
                {
                    il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                }

                il.EmitCall(OpCodes.Call, setMethod, null);
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Box, type);
            }
            else
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
                il.Emit(OpCodes.Ldarg_1);

                if (propertyInfo.PropertyType.IsClass)
                {
                    il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
                }
                else
                {
                    il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                }

                il.EmitCall(OpCodes.Callvirt, setMethod, null);
                il.Emit(OpCodes.Ldarg_0);
            }

            il.Emit(OpCodes.Ret);

            return (GenericSetterHandler)setter.CreateDelegate(typeof(GenericSetterHandler));
        }

        /// <summary>
        /// Creates a generic getter for the given field.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldInfo"></param>
        /// <returns></returns>
        private static GenericGetterHandler CreateGetField(Type type, FieldInfo fieldInfo)
        {
            DynamicMethod dynamicGet = new DynamicMethod("_", typeof(object), new Type[] { typeof(object) }, type, true);
            ILGenerator il = dynamicGet.GetILGenerator();

            if (!type.IsClass) // structs
            {
                var lv = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Unbox_Any, type);
                il.Emit(OpCodes.Stloc_0);
                il.Emit(OpCodes.Ldloca_S, lv);
                il.Emit(OpCodes.Ldfld, fieldInfo);
                if (fieldInfo.FieldType.IsValueType)
                    il.Emit(OpCodes.Box, fieldInfo.FieldType);
            }
            else
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, fieldInfo);
                if (fieldInfo.FieldType.IsValueType)
                    il.Emit(OpCodes.Box, fieldInfo.FieldType);
            }

            il.Emit(OpCodes.Ret);

            return (GenericGetterHandler)dynamicGet.CreateDelegate(typeof(GenericGetterHandler));
        }

        /// <summary>
        /// Creates a generic getter for the given property.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        private static GenericGetterHandler CreateGetMethod(Type type, PropertyInfo propertyInfo)
        {
            MethodInfo getMethod = propertyInfo.GetGetMethod();
            if (getMethod == null)
            {
                return null;
            }

            Type[] arguments = new Type[1];
            arguments[0] = typeof(object);

            DynamicMethod getter = new DynamicMethod("_", typeof(object), arguments, type);
            ILGenerator il = getter.GetILGenerator();

            if (!type.IsClass) // structs
            {
                var lv = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Unbox_Any, type);
                il.Emit(OpCodes.Stloc_0);
                il.Emit(OpCodes.Ldloca_S, lv);
                il.EmitCall(OpCodes.Call, getMethod, null);

                if (propertyInfo.PropertyType.IsValueType)
                {
                    il.Emit(OpCodes.Box, propertyInfo.PropertyType);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
                il.EmitCall(OpCodes.Callvirt, getMethod, null);

                if (propertyInfo.PropertyType.IsValueType)
                {
                    il.Emit(OpCodes.Box, propertyInfo.PropertyType);
                }
            }

            il.Emit(OpCodes.Ret);

            return (GenericGetterHandler)getter.CreateDelegate(typeof(GenericGetterHandler));
        }

        #endregion

        /// <summary>
        /// Returns the getters for the given type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal IEnumerable<GetterDescriptor> GetGetters(Type type)
        {
            IEnumerable<GetterDescriptor> result = null;
            if (_gettersCache.TryGetValue(type, out result))
            {
                return result;
            }

            var propertiesAndFields = GetPropertiesAndFields(type, null);

            List<GetterDescriptor> getters = new List<GetterDescriptor>();

            foreach (JsonPropertyInfo property in propertiesAndFields.Values)
            {
                GetterDescriptor gg = new GetterDescriptor();
                gg.Name = property.JsonFieldName;
                gg.Getter = property.Getter;
                gg.PropertyOrFieldType = property.PropertyOrFieldType;
                getters.Add(gg);
            }

            _gettersCache.Add(type, getters);

            return getters;
        }

        /// <summary>
        /// Returns the type information (from cache).
        /// </summary>
        /// <param name="targetType"></param>
        /// <returns></returns>
        internal JsonPropertyInfo GetInfo(Type targetType)
        {
            JsonPropertyInfo result;

            if (!_typeInfo.TryGetValue(targetType, out result))
            {
                result = CreateJsonPropertyInfo(targetType);

                _typeInfo.Add(targetType, result);
            }

            return result;
        }

        /// <summary>
        /// Returns the properties for the given types which participate in serialization.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="typename"></param>
        /// <returns></returns>
        internal RegistrySection<string, JsonPropertyInfo> GetPropertiesAndFields(Type type, string typename)
        {
            RegistrySection<string, JsonPropertyInfo> propertyDescription = null;

            if (typename == null) { typename = type.FullName; }

            if (_propertycache.TryGetValue(typename, out propertyDescription))
            {
                return propertyDescription;
            }
            else
            {
                propertyDescription = new RegistrySection<string, JsonPropertyInfo>();

                PropertyInfo[] pr = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo property in pr)
                {
                    if (!property.CanWrite && !_owner.serializationPolicy.HasFlag(SerializationPolicy.IncludeReadOnly)) { continue; }

                    if (!IsSerializationMember(property)) { continue; }

                    String jsonFieldName = GetJsonFieldName(property);
                    JsonPropertyInfo d = CreateJsonPropertyInfo(property.PropertyType);
                    d.CanWrite = property.CanWrite;
                    d.Getter = JsonRegistry.CreateGetMethod(type, property);
                    d.JsonFieldName = jsonFieldName;
                    d.PropertyOrFieldName = property.Name;

                    if (property.CanWrite)
                    {
                        d.Setter = JsonRegistry.CreateSetMethod(type, property);
                    }

                    propertyDescription.Add(jsonFieldName, d);
                    _typeInfo[d.PropertyOrFieldType] = d;
                }

                FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
                foreach (FieldInfo field in fields)
                {
                    if (!IsSerializationMember(field)) { continue; }

                    String jsonFieldName = GetJsonFieldName(field);
                    JsonPropertyInfo d = CreateJsonPropertyInfo(field.FieldType);
                    d.CanWrite = true;
                    d.Setter = JsonRegistry.CreateSetField(type, field);
                    d.Getter = JsonRegistry.CreateGetField(type, field);
                    d.PropertyOrFieldName = field.Name;
                    d.JsonFieldName = jsonFieldName;

                    propertyDescription.Add(jsonFieldName, d);
                    _typeInfo[d.PropertyOrFieldType] = d;
                }

                _propertycache.Add(typename, propertyDescription);

                return propertyDescription;
            }
        }

        private static String GetJsonFieldName(MemberInfo memberInfo)
        {
            var dataMemberAttribute = (DataMemberAttribute)memberInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).SingleOrDefault();

            if (dataMemberAttribute != null)
            {
                return (!String.IsNullOrEmpty(dataMemberAttribute.Name)) ? dataMemberAttribute.Name : memberInfo.Name;
            }
            else
            {
                return memberInfo.Name;
            }
        }

        private bool IsSerializationMember(MemberInfo memberInfo)
        {
            object[] attributes = memberInfo.GetCustomAttributes(false);

            if (_owner.serializationPolicy.HasFlag(SerializationPolicy.PropertyOptIn))
            {
                if (attributes.OfType<DataMemberAttribute>().Any())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (_owner.serializationPolicy.HasFlag(SerializationPolicy.PropertyOptOut))
            {
                if (attributes.OfType<XmlIgnoreAttribute>().Any() || attributes.OfType<IgnoreDataMemberAttribute>().Any())
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }

            return true;
        }
    }
}

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 Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions