UTC
大石头 authored at 2020-01-07 18:17:54
13.24 KiB
X
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using NewLife.Reflection;

namespace NewLife.Serialization
{
    /// <summary>Json读取器</summary>
    public class JsonReader
    {
        #region 属性
        /// <summary>是否使用UTC时间</summary>
        public Boolean UseUTCDateTime { get; set; }
        #endregion

        #region 构造
        //public JsonReader() { }
        #endregion

        #region 转换方法
        /// <summary>读取Json到指定类型</summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="json"></param>
        /// <returns></returns>
        public T Read<T>(String json)
        {
            return (T)Read(json, typeof(T));
        }

        /// <summary>读取Json到指定类型</summary>
        /// <param name="json"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public Object Read(String json, Type type)
        {
            // 解码得到字典或列表
            var obj = new JsonParser(json).Decode();
            if (obj == null) return null;

            return ToObject(obj, type, null);
        }

        /// <summary>Json字典或列表转为具体类型对象</summary>
        /// <param name="jobj">Json对象</param>
        /// <param name="type">模板类型</param>
        /// <param name="target">目标对象</param>
        /// <returns></returns>
        public Object ToObject(Object jobj, Type type, Object target)
        {
            if (type == null && target != null) type = target.GetType();

            if (type.IsAssignableFrom(jobj.GetType())) return jobj;

            // Json对象是字典,目标类型可以是字典或复杂对象
            if (jobj is IDictionary<String, Object> vdic)
            {
                if (type.IsDictionary())
                    return ParseDictionary(vdic, type, target as IDictionary);
                else
                    return ParseObject(vdic, type, target);
            }

            // Json对象是列表,目标类型只能是列表或数组
            if (jobj is IList<Object> vlist)
            {
                if (type.IsList()) return ParseList(vlist, type, target);
                if (type.IsArray) return ParseArray(vlist, type, target);
                // 复杂键值的字典,也可能保存为Json数组
                if (type.IsDictionary()) return CreateDictionary(vlist, type, target);

                if (vlist.Count == 0) return target;

                throw new InvalidCastException($"Json数组无法转为目标类型[{type.FullName}],仅支持数组和List<T>/IList<T>");
            }

            if (type != null && jobj.GetType() != type)
                return ChangeType(jobj, type);

            return jobj;
        }
        #endregion

        #region 复杂类型
        /// <summary>转为泛型列表</summary>
        /// <param name="vlist"></param>
        /// <param name="type"></param>
        /// <param name="target">目标对象</param>
        /// <returns></returns>
        private IList ParseList(IList<Object> vlist, Type type, Object target)
        {
            var elmType = type.GetGenericArguments().FirstOrDefault();

            // 处理一下type是IList<>的情况
            if (type.IsInterface) type = typeof(List<>).MakeGenericType(elmType);

            // 创建列表
            var list = (target ?? type.CreateInstance()) as IList;
            foreach (var item in vlist)
            {
                if (item == null) continue;

                var val = ToObject(item, elmType, null);
                list.Add(val);
            }
            return list;
        }

        /// <summary>转为数组</summary>
        /// <param name="list"></param>
        /// <param name="type"></param>
        /// <param name="target">目标对象</param>
        /// <returns></returns>
        private Array ParseArray(IList<Object> list, Type type, Object target)
        {
            var elmType = type?.GetElementTypeEx();
            if (elmType == null) elmType = typeof(Object);

            var arr = target as Array;
            if (arr == null) arr = Array.CreateInstance(elmType, list.Count);
            // 如果源数组有值,则最大只能创建源数组那么多项,抛弃多余项
            for (var i = 0; i < list.Count && i < arr.Length; i++)
            {
                var item = list[i];
                if (item == null) continue;

                var val = ToObject(item, elmType, arr.GetValue(i));
                arr.SetValue(val, i);
            }

            return arr;
        }

        /// <summary>转为泛型字典</summary>
        /// <param name="dic"></param>
        /// <param name="type"></param>
        /// <param name="target">目标对象</param>
        /// <returns></returns>
        private IDictionary ParseDictionary(IDictionary<String, Object> dic, Type type, IDictionary target)
        {
            var types = type.GetGenericArguments();

            if (target == null)
            {
                // 处理一下type是Dictionary<,>的情况
                if (type.IsInterface) type = typeof(Dictionary<,>).MakeGenericType(types[0], types[1]);

                target = type.CreateInstance() as IDictionary;
            }
            foreach (var kv in dic)
            {
                var key = ToObject(kv.Key, types[0], null);
                var val = ToObject(kv.Value, types[1], null);
                target.Add(key, val);
            }

            return target;
        }

        private Dictionary<Object, Int32> _circobj = new Dictionary<Object, Int32>();
        private Dictionary<Int32, Object> _cirrev = new Dictionary<Int32, Object>();
        /// <summary>字典转复杂对象,反射属性赋值</summary>
        /// <param name="dic"></param>
        /// <param name="type"></param>
        /// <param name="target">目标对象</param>
        /// <returns></returns>
        internal Object ParseObject(IDictionary<String, Object> dic, Type type, Object target)
        {
            if (type == typeof(NameValueCollection)) return CreateNV(dic);
            if (type == typeof(StringDictionary)) return CreateSD(dic);
            if (type == typeof(Object)) return dic;

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

            if (type.IsDictionary()) return CreateDic(dic, type, target);

            if (!_circobj.TryGetValue(target, out var circount))
            {
                circount = _circobj.Count + 1;
                _circobj.Add(target, circount);
                _cirrev.Add(circount, target);
            }

            // 遍历所有可用于序列化的属性
            var props = type.GetProperties(true).ToDictionary(e => SerialHelper.GetName(e), e => e);
            foreach (var item in dic)
            {
                var v = item.Value;
                if (v == null) continue;

                if (!props.TryGetValue(item.Key, out var pi))
                {
                    // 可能有小写
                    pi = props.Values.FirstOrDefault(e => e.Name.EqualIgnoreCase(item.Key));
                    if (pi == null) continue;
                }
                if (!pi.CanWrite) continue;

                var pt = pi.PropertyType;
                if (pt.GetTypeCode() != TypeCode.Object)
                    target.SetValue(pi, ChangeType(v, pt));
                else
                {
                    var orig = target.GetValue(pi);
                    var val = ToObject(v, pt, orig);
                    if (val != orig) target.SetValue(pi, val);
                }
            }
            return target;
        }
        #endregion

        #region 辅助
        private Object ChangeType(Object value, Type type)
        {
            // 支持可空类型
            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null) return value;

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

            if (type == typeof(Int32)) return value.ToInt();
            if (type == typeof(Int64)) return value.ToLong();
            if (type == typeof(String)) return value + "";

            if (type.IsEnum) return Enum.Parse(type, value + "");
            if (type == typeof(DateTime)) return CreateDateTime(value);

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

            if (type == typeof(Byte[]))
            {
                if (value is Byte[]) return (Byte[])value;

                return Convert.FromBase64String(value + "");
            }

            if (type == typeof(TimeSpan)) return TimeSpan.Parse(value + "");

            if (type.GetTypeCode() == TypeCode.Object) return null;

            return value.ChangeType(type);
        }

        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 IDictionary CreateDic(IDictionary<String, Object> dic, Type type, Object obj)
        {
            var nv = obj as IDictionary;
            if (type.IsGenericType && type.GetGenericArguments().Length >= 2)
            {
                var tval = type.GetGenericArguments()[1];
                foreach (var item in dic)
                    nv.Add(item.Key, item.Value.ChangeType(tval));
            }
            else
            {
                foreach (var item in dic)
                    nv.Add(item.Key, 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 += cc - '0';
                }
            }
            if (neg) num = -num;

            return num;
        }

        /// <summary>创建时间</summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private DateTime CreateDateTime(Object value)
        {
            if (value is DateTime) return (DateTime)value;
            if (value is Int64 || value is Int32)
            {
                var dt = value.ToDateTime();
                if (UseUTCDateTime) dt = dt.ToUniversalTime();
                return dt;
            }

            //用于解决奇葩json中时间字段使用了utc时间戳,还是用双引号包裹起来的情况。
            if (value is String)
            {
                if (Int64.TryParse(value + "", out var result) && result > 0)
                {
                    var sdt = result.ToDateTime();
                    if (UseUTCDateTime) sdt = sdt.ToUniversalTime();
                    return sdt;
                }
            }

            var str = (String)value;
            if (str.IsNullOrEmpty()) return DateTime.MinValue;

            var utc = false;

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

            year = CreateInteger(str, 0, 4);
            month = CreateInteger(str, 5, 2);
            day = CreateInteger(str, 8, 2);
            if (str.Length >= 19)
            {
                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' || str.EndsWithIgnoreCase("UTC")) 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 CreateDictionary(IList<Object> list, Type type, Object target)
        {
            var types = type.GetGenericArguments();
            var dic = (target ?? type.CreateInstance()) as IDictionary;
            foreach (IDictionary<String, Object> values in list)
            {
                var key = ToObject(values["k"], types[0], null);
                var val = ToObject(values["v"], types[1], null);
                dic.Add(key, val);
            }

            return dic;
        }
        #endregion
    }
}