导航
- 前言
- 抛出问题
- 分析问题
- 解决思路
- 结语
- 参考
感谢您的阅读,预计阅读时长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类型。
至此,问题得以完美解决。
结语
遇到问题的时候,一开始就陷入技术细节,容易导致我们迷失方向,甚至走弯路。保持冷静,从问题现象和场景出发,逐步找到产生问题的根源,然后一击即中。