gson线上环境解析日期时报错JsonSyntaxException

遇到问题:

问题发生在新功能上线的那一天,所有的测试工作都测试完了,测试还挺顺利,比以往结束的早很多,正高高兴兴的聊着今天可以早回家了。

正高兴着往ST环境部署,部署上去前端一测试却返回了服务器异常,服务器异常信息是这样的:

这里写图片描述
这里写图片描述
是用了gson解析json串,解析过程中出现了JsonSyntaxException,也给出了待解析的数据值,是一个常见的日期字符串“2018-03-14 00:00:00”。

这时候第一反应是,测试覆盖不够,没有测试到这种情况?或者是测试偷懒了,每次都用的假数据,没有完整走完测试路径?

虽然是第一想法,但是,作为有经验的开发,我们应该相信测试的专业度,以及测试最基本的功能测试,况且是正向流程的测试,是不会出现问题的。在遇到问题时,我们应该首先选择相信同事。

这种情况,还有另外一种可能性就是测试环境和线上环境不一样。

应急处理:

从上述两个问题出发,一个是确认同事真的走了完整的测试流程;另一个是确认测试和ST环境有什么不同。

第一个问题,从提测开始的请求日志进行了逐个的查看,发现测试的最后两天确实请求了同样的接口,并且返回了正确的数据,说明这个问题不是测试的锅,而是开发自己的锅。

经过百度和google搜索,发现有不少人遇到了同样的异常,问题的原因是因为服务器的Locale不一样导致的,也就是说gson解析日期的方法是会受到Locale影响的。

但是对比测试服务器和线上服务器的Locale发现是一样的,那么想从根本解决问题就变的比较难以下手。

为了赶上上线的节点,我们选择了把日期解析成字符串类型,然后在返给前端之前再使用joda的DateTimeFormatter进行一次日期格式的解析。迅速修改,迅速上线。

追寻原因:

做为非算法类工程师,唯一能够迅速提升的方法就是:抓住问题,不断深挖,找到问题的原因,解决问题。所以,放过这个问题,绝对是巨大的损失。

打开gson的源代码,找到抛出异常的位置:
这里写图片描述

可以看到抛出JsonSyntaxException是由于ISO8601Utils.parse方法内部出现的异常。再进入到parse方法中,发现它的功能很强大,但是可解析的日期格式却不包含我们的日期字符串样式:
这里写图片描述

ISO 8601是国际标准化组织提出的日期和时间的国际标准表示方法。从gson中的parse方法来看,大致逻辑是这样的:
1.如果日期值只有年月日(yyyy-MM-dd或者yyyyMMdd),直接返回年月日对应的Date对象;
2.如果日期字符串解析完年月日之后,还有多余的字符,那么按照有time zone进行解析,并获取time zone;

问题就出在第二点上,我们的日期格式除了年月日字符之外还有时分秒字符,但是却没有timezone标记。从源代码来看,我们的日期字符串不可能被成功解析,也就是测试环境也应该出异常才合理。

继续,我们做了一个做简单的示例程序,只有一个main方法,使用gson对我们的日期字符串”{“dt”:”2018-03-14 00:00:00”}”进行解析。

结果是:在台式开发机上确实没有异常,但是放到服务器上运行就会报异常。有了明确的差异,我们就比较容易定位问题了!

在台式开发机上debug跟踪代码,开发机上没有运行错误的代码行,而是执行了parse中的前半部分代码,我们再回顾一下parse中的代码行:
这里写图片描述

也就是开发机上执行的代码行是dateFormat.parse(json),说明dateFormats中有能够解析我们的日期格式的DateFormat类型。再跟踪一下dateFormats的初始化的代码:

这里写图片描述

这里默认初始化了一个Locale.US的dateFormat,初始化了一个默认非US的Locale,还有一个Java90rLater的。

当在开发机上运行的时候,会创建一个Locale=zh_CN.UTF-8的DateFormat是这个解析类,解析了我们的日期字符串“2018-03-14 00:00:00”。

我们的ST环境服务器上采用的是en_US.UTF-8,jdk1.7,所以只要一个Locale.US的日期解析器,他不能解析“2018-03-14 00:00:00”格式的日期字符串。

所以会执行ISO8601.parse去解析,代码逻辑定位清楚了,问题原因就是ST环境服务器上的locale不是中文,所以解析不了“2018-03-14 00:00:00”样式的日期字符串。

然而,我们的测试环境也是en_US.UTF-8的Locale,但为什么测试过程中没有出错呢?

原来,测试环境部署的人用的是Xterm,ST环境部署的人用的是secureCRT,登录到同一台机器上之后,当前shell的Locale是不一样的,所以出现了这种诡异的情况。

好了,解决方案还是保留我们应急的解决方案,也就是gson解析成String日期字符串,再用joda的DateTimeFormatter解析成Date类型。因为这种方案不会受到服务器环境的影响。

当然,你也可以像这篇博客那样去改服务器的默认Locale。只是这样的话,仍然存在风险,比如,其他人用的shell客户端登录之后,当前shell默认成en_US就会有问题了!

同类对比:

那么,jackson有没有同样的问题呢?jackson解析日期也需要指定特定的解析器才行,如果用默认的解析器,也会遇到类似无法解析的情况,比如这篇博客写到的案例

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页