v9.6.2017.0808   重构正向工程,基于映射表查找数据库字段类型到实体类型的映射
大石头 编写于 2017-08-08 21:38:06
X
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Reflection;
using NewLife.Reflection;

namespace NewLife.Serialization
{
    /// <summary>Json读取器</summary>
    internal class JsonReader
    {
        #region 属性
        public Boolean UseUTCDateTime { get; set; }

        #endregion

        #region 构造
        public JsonReader()
        {
            UseUTCDateTime = false;
        }
        #endregion

        #region 转换方法
        public T ToObject<T>(String json)
        {
            return (T)ToObject(json, typeof(T));
        }

        public Object ToObject(String json, Type type)
        {
            var obj = new JsonParser(json).Decode();
            if (obj == null) return null;

            return ToObject(obj, type);
        }

        public Object ToObject(Object jobj, Type type)
        {
            Type typeDef = null;
            if (type != null && type.IsGenericType) typeDef = type.GetGenericTypeDefinition();

            if (jobj is IDictionary)
            {
                if (type != null && typeDef == typeof(IDictionary<,>)) // 字典
                    return RootDictionary(jobj, type);
                else
                    return Parse(jobj as IDictionary<String, Object>, type, null);
            }
            else if (jobj is IList<Object>)
            {
                if (type != null && typeDef == typeof(Dictionary<,>)) // 名值格式
                    return RootDictionary(jobj, type);
                else if (type != null && typeDef == typeof(List<>)) // 泛型列表
                    return RootList(jobj, type);
                else
                {
                    var elmType = type.GetElementTypeEx();
                    //return (jobj as IList<Object>).Select(e => e.ChangeType(elmType)).ToArray();
                    var src = jobj as IList<Object>;
                    var arr = Array.CreateInstance(elmType, src.Count);
                    for (var i = 0; i < src.Count; i++)
                    {
                        if (Type.GetTypeCode(elmType) != TypeCode.Object)
                            arr.SetValue(src[i].ChangeType(elmType), i);
                        else
                            arr.SetValue(ToObject(src[i], elmType), i);
                    }

                    return arr;
                }
            }
            else if (type == typeof(Byte[]))
            {
                if (jobj is Byte[]) return (Byte[])jobj;

                return Convert.FromBase64String(jobj + "");
            }
            else if (type != null && jobj.GetType() != type)
                return ChangeType(jobj, type);

            return jobj;
        }
        #endregion

        #region 辅助
        private Object ChangeType(Object value, Type type)
        {
            if (type == typeof(Int32))
                return (Int32)((Int64)value);

            else if (type == typeof(Int64))
                return (Int64)value;

            else if (type == typeof(String))
                return (String)value;

            else if (type.IsEnum)
                return Enum.Parse(type, value + "");

            else if (type == typeof(DateTime))
                return CreateDateTime(value);

            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null) return value;

                type = type.GetGenericArguments()[0];
            }

            if (type == typeof(Guid)) return new Guid((String)value);

            return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
        }

        private Object RootList(Object obj, Type type)
        {
            var types = type.GetGenericArguments();
            var list = type.CreateInstance() as IList;
            foreach (var item in (IList)obj)
            {
                var value = item;
                if (item is IDictionary<String, Object>)
                    value = Parse(item as IDictionary<String, Object>, types[0], null);
                else
                    value = ChangeType(item, types[0]);

                list.Add(value);
            }
            return list;
        }

        private Object RootDictionary(Object obj, Type type)
        {
            var types = type.GetGenericArguments();
            Type tkey = null;
            Type tval = null;
            if (types != null)
            {
                tkey = types[0];
                tval = types[1];
            }
            if (obj is IDictionary<String, Object>)
            {
                var dic = type.CreateInstance() as IDictionary;

                foreach (var kv in (IDictionary<String, Object>)obj)
                {
                    Object val;

                    if (kv.Value is IDictionary<String, Object>)
                        val = Parse(kv.Value as IDictionary<String, Object>, tval, null);

                    else if (tval.IsArray)
                        val = CreateArray((IList<Object>)kv.Value, tval, tval.GetElementType());

                    else if (kv.Value is IList)
                        val = CreateGenericList((IList<Object>)kv.Value, tval, tkey);

                    else
                        val = ChangeType(kv.Value, tval);

                    var key = ChangeType(kv.Key, tkey);
                    dic.Add(key, val);
                }

                return dic;
            }
            if (obj is IList<Object>) return CreateDictionary(obj as IList<Object>, type, types);

            return null;
        }

        private Dictionary<Object, Int32> _circobj = new Dictionary<Object, Int32>();
        private Dictionary<Int32, Object> _cirrev = new Dictionary<Int32, Object>();
        internal Object Parse(IDictionary<String, Object> dic, Type type, Object obj)
        {
            if (type == typeof(NameValueCollection)) return CreateNV(dic);
            if (type == typeof(StringDictionary)) return CreateSD(dic);
            if (type == typeof(Object)) return dic;

            if (obj == null) obj = type.CreateInstance();

            var circount = 0;
            if (_circobj.TryGetValue(obj, out circount) == false)
            {
                circount = _circobj.Count + 1;
                _circobj.Add(obj, circount);
                _cirrev.Add(circount, obj);
            }

            var props = type.GetProperties(true).ToDictionary(e => e.Name, e => e);
            foreach (var item in dic)
            {
                var v = item.Value;
                PropertyInfo pi;
                if (!props.TryGetValue(item.Key, out pi))
                {
                    // 可能有小写
                    pi = props.Values.Where(e => e.Name.EqualIgnoreCase(item.Key)).FirstOrDefault();
                    if (pi == null) continue;
                }
                if (!pi.CanWrite) continue;

                if (v == null) continue;

                Object val = null;

                var vdic = v as IDictionary<String, Object>;

                var pt = pi.PropertyType;
                // 支持可空类型
                pt = Nullable.GetUnderlyingType(pt) ?? pt;
                if (pt.IsEnum)
                    val = Enum.Parse(pt, v + "");
                else if (pt == typeof(Object))
                    val = v;
                else if (pt == typeof(DateTime))
                    val = CreateDateTime(v);
                else if (pt == typeof(Guid))
                    val = new Guid((String)v);
                else if (pt == typeof(Byte[]))
                    val = Convert.FromBase64String((String)v);
                else if (pt.IsArray)
                    val = CreateArray((IList<Object>)v, pt, pt.GetElementTypeEx());
                else if (pt.As<IList>())
                    val = CreateGenericList((IList<Object>)v, pt, pt.GetElementTypeEx());
                else if (pt.IsGenericType && typeof(Dictionary<,>).IsAssignableFrom(pt.GetGenericTypeDefinition()))
                    val = CreateStringKeyDictionary(vdic, pt, pt.GetGenericArguments());
                else if (pt.As<IDictionary>())
                    val = CreateDictionary((IList<Object>)v, pt, pt.GetGenericArguments());
                else if (pt == typeof(NameValueCollection))
                    val = CreateNV(vdic);
                else if (pt == typeof(StringDictionary))
                    val = CreateSD(vdic);
                else if (Type.GetTypeCode(pt) != TypeCode.Object)
                    val = v;
                else
                {
                    // 内嵌对象
                    val = Parse(vdic, pt, obj.GetValue(pi));

                    //throw new NotSupportedException();
                }

                obj.SetValue(pi, val);
            }
            return obj;
        }

        private StringDictionary CreateSD(IDictionary<String, Object> dic)
        {
            var nv = new StringDictionary();
            foreach (var item in dic)
                nv.Add(item.Key, (String)item.Value);

            return nv;
        }

        private NameValueCollection CreateNV(IDictionary<String, Object> dic)
        {
            var nv = new NameValueCollection();
            foreach (var item in dic)
                nv.Add(item.Key, (String)item.Value);

            return nv;
        }

        private Int32 CreateInteger(String str, Int32 index, Int32 count)
        {
            var num = 0;
            var neg = false;
            for (var x = 0; x < count; x++, index++)
            {
                var cc = str[index];

                if (cc == '-')
                    neg = true;
                else if (cc == '+')
                    neg = false;
                else
                {
                    num *= 10;
                    num += (Int32)(cc - '0');
                }
            }
            if (neg) num = -num;

            return num;
        }

        private DateTime CreateDateTime(Object value)
        {
            if (value is DateTime) return (DateTime)value;

            if (value is Int64 || value is Int32)
            {
                var num = Convert.ToInt64(value);
                var dt = new DateTime(1970, 1, 1);
                if (num > 100 * 365 * 24 * 3600L)
                    return dt.AddMilliseconds(num);
                else
                    return dt.AddSeconds(num);
            }

            var str = (String)value;

            var utc = false;

            Int32 year;
            Int32 month;
            Int32 day;
            Int32 hour;
            Int32 min;
            Int32 sec;
            var ms = 0;

            year = CreateInteger(str, 0, 4);
            month = CreateInteger(str, 5, 2);
            day = CreateInteger(str, 8, 2);
            hour = CreateInteger(str, 11, 2);
            min = CreateInteger(str, 14, 2);
            sec = CreateInteger(str, 17, 2);
            if (str.Length > 21 && str[19] == '.')
                ms = CreateInteger(str, 20, 3);

            if (str[str.Length - 1] == 'Z') utc = true;

            if (!UseUTCDateTime && !utc)
                return new DateTime(year, month, day, hour, min, sec, ms);
            else
                return new DateTime(year, month, day, hour, min, sec, ms, DateTimeKind.Utc).ToLocalTime();
        }

        private Object CreateArray(IList<Object> list, Type type, Type elmType)
        {
            if (elmType == null) elmType = typeof(Object);

            var arr = Array.CreateInstance(elmType, list.Count);
            for (var i = 0; i < list.Count; i++)
            {
                var ob = list[i];
                if (ob == null)
                {
                    continue;
                }
                if (ob is IDictionary)
                    arr.SetValue(Parse((IDictionary<String, Object>)ob, elmType, null), i);
                else if (ob is ICollection)
                    arr.SetValue(CreateArray((IList<Object>)ob, elmType, elmType.GetElementType()), i);
                else
                    arr.SetValue(ChangeType(ob, elmType), i);
            }

            return arr;
        }

        private Object CreateGenericList(IList<Object> list, Type type, Type elmType)
        {
            var rs = type.CreateInstance() as IList;
            foreach (var ob in list)
            {
                if (ob is IDictionary)
                    rs.Add(Parse((IDictionary<String, Object>)ob, elmType, null));

                else if (ob is IList<Object>)
                {
                    if (elmType.IsGenericType)
                        rs.Add((IList<Object>)ob);
                    else
                        rs.Add(((IList<Object>)ob).ToArray());
                }
                else
                    rs.Add(ChangeType(ob, elmType));
            }
            return rs;
        }

        private Object CreateStringKeyDictionary(IDictionary<String, Object> dic, Type pt, Type[] types)
        {
            var rs = pt.CreateInstance() as IDictionary;
            Type tkey = null;
            Type tval = null;
            if (types != null)
            {
                tkey = types[0];
                tval = types[1];
            }

            foreach (var item in dic)
            {
                var key = item.Key;
                Object val = null;

                if (item.Value is IDictionary<String, Object>)
                    val = Parse((IDictionary<String, Object>)item.Value, tval, null);

                else if (types != null && tval.IsArray)
                {
                    if (item.Value is Array)
                        val = item.Value;
                    else
                        val = CreateArray((IList<Object>)item.Value, tval, tval.GetElementType());
                }
                else if (item.Value is IList)
                    val = CreateGenericList((IList<Object>)item.Value, tval, tkey);

                else
                    val = ChangeType(item.Value, tval);

                rs.Add(key, val);
            }

            return rs;
        }

        private Object CreateDictionary(IList<Object> list, Type pt, Type[] types)
        {
            var dic = pt.CreateInstance() as IDictionary;
            Type tkey = null;
            Type tval = null;
            if (types != null)
            {
                tkey = types[0];
                tval = types[1];
            }

            foreach (IDictionary<String, Object> values in list)
            {
                var key = values["k"];
                var val = values["v"];

                if (key is IDictionary<String, Object>)
                    key = Parse((IDictionary<String, Object>)key, tkey, null);
                else
                    key = ChangeType(key, tkey);

                if (val is IDictionary<String, Object>)
                    val = Parse((IDictionary<String, Object>)val, tval, null);
                else
                    val = ChangeType(val, tval);

                dic.Add(key, val);
            }

            return dic;
        }
        #endregion
    }
}