序言
JSON作为一种轻量级的数据交换格式,基于ECMAScript的一个子集设计,采用完全独立于编程语言的文本格式来表述数据 。它具备易于人类阅读和编写,同时也便于机器解析和生成的特性,这使得其在数据交换场景中展现出极高的效率 。在日常开发里,JSON已然成为程序员们每日都会用到的小类库。无论是谷歌的gson、阿里巴巴的fastjson、框架自带的jackjson,亦或是第三方hutool的json等,大家都频繁地与之打交道。然而,即便如此熟悉,有时也难免在看似简单的地方遭遇问题。
1. 平平无奇的接口
/**
* 获取vehicleinfo信息
*
* @param vehicleId
* @return Vehicle的json字符串
*/
String loadVehicleInfo(Integer vehicleId);
这个接口的功能很单纯,通过传入vehicleId参数,获取对应的Vehicle对象,随后将该对象信息序列化成JSON字符串并返回。
2. 看似无懈可击的引用
String jsonStr = auctVehicleService.loadVehicleInfo(freezeDetail.getVehicle().getId());
if (StringUtils.isNotBlank(jsonStr)) {
Vehicle vehicle = JSON.parseObject(jsonStr, Vehicle.class);
if (vehicle != null) {
// 后续省略...
}
}
乍看之下,这段引用代码似乎十分稳健。对jsonStr进行了非空字符串判断,对解析后的vehicle对象也做了非空判断,看似已杜绝了空指针异常的隐患。但实际上,其中暗藏玄机。
3. 故障引发
线上突然出现了类似故障(此处报错信息为线下模拟所得)。而在测试阶段,一切看似正常。究其原因,是因为当时主要测试基础数据,测试数据中恰好未涉及Date类型的数据,所以线下并未察觉问题。
4. 故障原因分析
从报错日志能够清晰看出,问题根源在于日期类型的参数。类似Mar 24, 2025 1:23:10 PM这样的日期格式,无法被Fastjson正常解析。深入查看相关代码:
@Override
public String loadVehicleInfo(Integer vehicleId) {
String key = VEHICLE_KEY + vehicleId;
Object obj = cacheService.get(key);
if (null != obj && StringUtils.isNotEmpty(obj.toString())
&&!"null".equals(obj.toString())) {
String result = (String) obj;
return result;
}
String json = null;
try {
Vehicle vInfo = overrideVehicleAttributes(vehicleId);
// 堆代码 duidaima.com
// 使用了Gson序列化对象
json = gson.toJson(vInfo);
cacheService.setExpireSec(key, gson.toJson(vInfo), 5 * 60);
} catch (Exception e) {
cacheService.setExpireSec(key, "", 1 * 60);
} finally {
}
return json;
}
原来,接口实现在返回对象时,采用谷歌的Gson进行了序列化操作。但在调用处,却使用阿里巴巴的Fastjson进行反序列化,这就导致了参数解析异常,从而引发线上故障。一旦出现此类问题,很可能面临绩效扣分、工资被扣的情况,着实令人头疼。
5. 小结
别看这只是个小问题,但其造成的影响不容小觑。一直以来,业界都在探讨程序员为何难以写出毫无bug的程序,从这样的案例中或许能窥见一斑。此次经历,真可谓“肉疼”,不仅影响工作成果,还让钱包受了损失 。