• 接口使用Gson进行序列化,调用时却使用Fastjson进行反序列化导致的Bug
  • 发布于 3天前
  • 28 热度
    0 评论
序言
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的程序,从这样的案例中或许能窥见一斑。此次经历,真可谓“肉疼”,不仅影响工作成果,还让钱包受了损失 。
用户评论