NewtoJson反序列化Unix时间戳(timestamp)为DateTime
  楠木大叔   4/10/21 10:32:39 AM
因为掌握技术这把“锤子”,我们就容易看什么都像“钉子”,从而钻进牛角尖,一发不可收拾。

导航

  • 前言
  • 抛出问题
  • 分析问题
  • 解决思路
  • 结语
  • 参考

感谢您的阅读,预计阅读时长3min。 智客工坊出品必属精品。

前言

我们迷茫,因为我们总是陷入细节。

技术人员遇到问题是常态,有些是新问题,有些是老问题。在着手解决问题之前,建议跳出技术思维,先厘清问题出现的场景。因为掌握技术这把“锤子”,我们就容易看什么都像“钉子”,从而钻进牛角尖,一发不可收拾

抛出问题

对象的序列化和反序列化是经常使用的。最近在前端同时反馈一个报错:


Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: 1. Path 'sendTime', line 1, position 202.   at 

Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)

原来是这段报出的异常:

JsonConvert.DeserializeObject<Msg>(json)

从报错异常来看就是反序列化时候,字段sendTime转换异常。这是这么回事呢?

分析问题

经过排查发现,序列化的对象中sendtime类型是Datetime类型,


...省略其他字段

/// <summary>
/// 发送时间
/// </summary>
[Description("发送时间")]
public DateTime SendTime { get; set; }

但是Json中的sendtime是timestamp类型。

{
   ...省略其他字段
   "sendtime":1618018360
}

到这里,就要补充一个关键背景。项目早期是由C#开发的,后面又加入了Java开发了部分接口,恰好两种语言都向同一个Redis读取数据。Java写入日期类型是timestamp类型。C#写入的是datetime类型。

Notes:在实际应用中,java的Jackson应该是做了兼容,有兴趣的同学可以去了解一下。

解决方案

问题的焦点就是NewtonJson反序列化的时候,不能转换timestamp类型为datetime类型

这个时候笔者的第一想法是让Java团队修改存储格式为datetime,但是那样jjava相关代码改动会比较大,并且Jackson支持datetime和timestamp转化。权衡之后,决定在.NET代码上做兼容。但是应该这么做呢?

研究了一下NewtonJson的DeserializeObject方法有如下重载:

        //
        // 摘要:
        //     Deserializes the JSON to the specified .NET type using a collection of Newtonsoft.Json.JsonConverter.
        //
        // 参数:
        //   value:
        //     The JSON to deserialize.
        //
        //   type:
        //     The type of the object to deserialize.
        //
        //   converters:
        //     Converters to use while deserializing.
        //
        // 返回结果:
        //     The deserialized object from the JSON string.
        [DebuggerStepThrough]
        public static object DeserializeObject(string value, Type type, params JsonConverter[] converters);

JsonConverter是什么鬼?是否可以解决我们的问题。于是搜索一下,找到了这个《Newton.Json中JsonConverter的使用》

其中这段给我启示:

class MyJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(SandBoxGanmeBase).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            var jsonObject = JObject.Load(reader);
            object target = null;
            JToken gameName;
            if (jsonObject.TryGetValue("Name", out gameName))
            {
                switch (gameName.ToString())
                {
                    case "GTA5":
                        target = new GTA5();
                        break;
                    case "Cyberpunk2077":
                        target = new Cyberpunk2077();
                        break;
                }
            }
            serializer.Populate(jsonObject.CreateReader(), target);
            return target;
        }
        catch (Exception ex)
        {
            throw new Exception("解析异常:" + ex.Message);
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}

照这个思路,我也做了个DateTimeJsonConverter


public class DateTimeJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(DateTime);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            TypeCode typeCode = Type.GetTypeCode(reader.Value.GetType());
            if (typeCode == TypeCode.DateTime)
                return reader.Value;

            var t = long.Parse((string)(reader.Value.ToString()));
            return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }

自定义Converter,将timestamp转换成了Datetime类型。

至此,问题得以完美解决。

结语

遇到问题的时候,一开始就陷入技术细节,容易导致我们迷失方向,甚至走弯路。保持冷静,从问题现象和场景出发,逐步找到产生问题的根源,然后一击即中。

参考:

版权声明: 本文为智客工坊「楠木大叔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。