技术思绪摘录旅行笔记
Newtonsoft.Json 大多数人都用过,几乎每个项目都需要这个东西,他的作用是实现对象之间的转换,用得最多的就是json字符串和C#对象之间的转换,今天就来说一下,当遇到他转换不了的时候,我们怎么处理?

Newtonsoft.Json 源码地址:https://github.com/JamesNK/Newtonsoft.Json

作者James Newton-King,现在在微软工作,所以你看.NetCore有些对象映射以及System.Text.Json的方法和用法都是一毛一样的。

image.png

从组织一栏,也能看出来,他服务的团队,跟我们用的技术息息相关。


我们先看下我们之前的封装,都干了啥

    public static class SerializerHelper
    {
        /// <summary>
        /// JSON序列化
        /// </summary>
        /// <param name="obj">对象</param>
        /// <returns>JSON字符串</returns>
        public static string SerializeObject(this object obj)
        {
            try
            {
                var jsonSerializerSettings = new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd HH:mm:ss" };
                return JsonConvert.SerializeObject(obj, jsonSerializerSettings);
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="json">JSON字符串</param>
        /// <returns>对象</returns>
        public static T DeserializeObject<T>(this string json)
        {
            try
            {
                return JsonConvert.DeserializeObject<T>(json);
            }
            catch
            {
                return default(T);
            }
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="json">JSON字符串</param>
        /// <returns>对象</returns>
        public static dynamic DeserializeObject(this string json)
        {
            try
            {
                return JsonConvert.DeserializeObject(json);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// XML序列化方式深复制
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="obj">对象</param>
        /// <returns>复制对象</returns>
        public static T DeepCopy<T>(this T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer xml = new XmlSerializer(typeof(T));
                xml.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                retval = xml.Deserialize(ms);
                ms.Close();
            }

            return (T)retval;
        }

        /// <summary>
        /// 将一个实体对象转换为另一个实体对象
        /// </summary>
        /// <typeparam name="T1">第一个实体对象</typeparam>
        /// <typeparam name="T2">第二个实体对象</typeparam>
        /// <param name="source">转换的实体对象</param>
        /// <returns></returns>
        public static T2 CopyToModel<T1, T2>(T1 source)
        {
            T2 model = default(T2);
            if (source == null)
            {
                return model;
            }
            PropertyInfo[] pi = typeof(T2).GetProperties();
            PropertyInfo[] pi1 = typeof(T1).GetProperties();

            model = Activator.CreateInstance<T2>();
            for (int i = 0; i < pi.Length; i++)
            {
                for (int j = 0; j < pi1.Length; j++)
                {
                    if (pi[i].Name == pi1[j].Name)
                    {
                        pi[i].SetValue(model, pi1[j].GetValue(source, null), null);
                    }
                }
            }
            return model;
        }
        /// <summary>
        /// 将一个实体对象转换为另一个实体对象
        /// </summary>
        /// <typeparam name="T1">第一个实体对象</typeparam>
        /// <typeparam name="T2">第二个实体对象</typeparam>
        /// <param name="source">转换的实体对象</param>
        /// <returns></returns>
        public static List<T2> CopyToModel<T1, T2>(List<T1> source)
        {
            List<T2> modelList = new List<T2>();
            if (!source.Any())
            {
                return modelList;
            }
            PropertyInfo[] pi = typeof(T2).GetProperties();
            PropertyInfo[] pi1 = typeof(T1).GetProperties();
            foreach (T1 obj in source)
            {
                T2 model = Activator.CreateInstance<T2>();
                for (int i = 0; i < pi.Length; i++)
                {
                    for (int j = 0; j < pi1.Length; j++)
                    {
                        if (pi[i].Name == pi1[j].Name)
                        {
                            pi[i].SetValue(model, pi1[j].GetValue(obj, null), null);
                        }
                    }
                }
                modelList.Add(model);
            }

            return modelList;
        }
    }

这个类其实满足了我们平时大部分需求,但是在.NetCore中,很多东西被封装到了底层,由对象映射直接处理,不需要我们写这些方法了,由System.Test.Json提供支持。

这个类针对的都是string格式和对象中的类型能对应的情况。

接下来说几个不满足的:

1、json字符串中是时间戳,但是实体类中类型是DateTime

2、json字符串中是“41.0”,但是实体类中是Int

3、再比如Mongodb中的那一堆类型,没有能直接操作的,都需要转换后才能使用,比如ObjectId、Decimal128

细看这几个类型,其实还是有那么点联系的,“41.0”首先是字符串,其次更加接近浮点数,最后才是整数;ObjectId其实是UUID的格式,对应的就是一串字符串;Decimal128对应的就是Decimal。

我以第一个时间戳字符串反序列化DateTime为例。

1、先定义自定义转换器

    public class DateTimeConverter : JsonConverter
    {
        /// <summary>
        ///转json字符串
        /// </summary>
        /// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter" /> to write to.</param>
        /// <param name="value">The value.</param>
        /// <param name="serializer">The calling serializer.</param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }

        /// <summary>
        ///字符串转对象
        /// </summary>
        /// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="existingValue">The existing value of object being read.</param>
        /// <param name="serializer">The calling serializer.</param>
        /// <returns>
        /// The object value.
        /// </returns>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
            JsonSerializer serializer)
        {
            try
            {
                JToken token = JToken.Load(reader);
                string time = token.ToObject<string>();
                DateTime dt;
                if (time.Length <= 13)
                {

                    dt = ConvertStringToDateTime(time);
                }
                else
                {
                    dt = Convert.ToDateTime(time);
                }

                return dt;
            }
            catch
            {
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// Determines whether this instance can convert the specified object type.
        /// </summary>
        /// <param name="objectType">Type of the object.</param>
        /// <returns>
        /// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
        /// </returns>
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(DateTime));
        }
        
        /// <summary>  
        /// 将c# DateTime时间格式转换为Unix时间戳格式  
        /// </summary>  
        /// <param name="time">时间</param>  
        /// <returns>long</returns>  
        public static long ConvertDateTimeToInt(System.DateTime time)
        {
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0));
            long t = (time.Ticks - startTime.Ticks) / 10000;   //除10000调整为13位      
            return t;
        }

        /// <summary>
        /// 时间戳转C#时间
        /// </summary>
        /// <param name="timeStamp">The time stamp.</param>
        /// <returns></returns>
        public static DateTime ConvertStringToDateTime(string timeStamp)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = long.Parse(timeStamp + "0000");
            TimeSpan toNow = new TimeSpan(lTime);
            return dtStart.Add(toNow);
        }
    }

这个转换器中,以下方法是必须要有的:

1、WriteJson:序列化时使用(对象转JSON字符串)

2、ReadJson:反序列化时使用(JSON字符串转对象)

3、CanConvert:确定转换器实例是否可以转换指定的对象类型


所以我们要实现时间戳字符串转DataTime,只需要着重于ReadJson方法的重写。

实体类中加个特性,如下:

    public class UserInfo
    {
        /// <summary>
        /// Gets or sets the name of the user.
        /// </summary>
        public string UserName { get; set; }


        /// <summary>
        /// Gets or sets the create time.
        /// </summary>
        [JsonConverter(typeof(DateTimeConverter))]
        public DateTime CreateTime { get; set; }
    }

可以看到我将字符串取到之后,判断了长度是否为时间戳,然后转换之后返回的。

使用如下:

string str = "{\"UserName\":\"123\",\"CreateTime\":\"1592542123500\"}";  
UserInfo item = str.DeserializeObject<UserInfo>();
str = item.SerializeObject();
UserInfo item2 = str.DeserializeObject<UserInfo>();
Console.WriteLine(item2.SerializeObject());

image.png

如果想DateTime能还原回时间戳,修改一下WriteJson方法即可。


如果遇到其他情况,做法差不多,都是取到源字符串,用自己的方法进行处理即可。

CarsonIT 微信扫码关注公众号 策略、创意、技术

留下您的脚步

 

最近评论

查看更多>>

站点统计

总文章数:275 总分类数:18 总评论数:88 总浏览数:127.72万

精选推荐

阅读排行

友情打赏

请打开您的微信,扫一扫