前言
最近在开发电子签名项目时遇到了一个特别头疼的问题,系统在处理JSON数据时突然报错,错误信息看起来很奇怪:syntax error : f
。经过一番排查,发现是FastJSON在处理特定数据时的一个坑。今天把这个问题记录下来,希望能帮到遇到类似问题的朋友。
问题现象
系统运行得好好的,突然就报错了:
css
com.alibaba.fastjson2.JSONException: syntax error : f
at com.alibaba.fastjson2.JSONReaderUTF16.readBoolValue(JSONReaderUTF16.java:6426)
at com.alibaba.fastjson2.JSONReader.read(JSONReader.java:2164)
at com.alibaba.fastjson2.JSON.parse(JSON.java:67)
at com.alibaba.fastjson2.JSON.toJSON(JSON.java:3506)
看到这个错误,第一反应是:什么鬼?JSON格式没问题啊,怎么就解析不了了?
问题代码
出问题的代码很简单,就是想从返回的数据中提取一个字段:
java
// 这行代码出问题了
com.alibaba.fastjson2.JSONObject contentJson =
(com.alibaba.fastjson2.JSONObject) com.alibaba.fastjson2.JSON.toJSON(invoke.getData().getContent());
String edocId = null;
edocId = contentJson.getString("formRecordId");
if (StringUtils.isBlank(edocId)) {
edocId = contentJson.getString("formRecordCode");
}
数据分析
接口返回的JSON数据看起来完全正常:
json
{
"id": 8745074954268373736,
"startAccountId": -8901286526055593580,
"generateType": null,
"prevNodeId": 175688901899730,
"formRecordId": 6003933439012269528,
"pcPageParam": null,
"dealTime": 0,
"nodeName": "管理员",
"formRecordCode": "6003933439012269528",
"extLong1": 0,
"overdueWorkingShow": null
}
乍一看没什么问题,但仔细观察会发现,formRecordId
的值是一个很大的数字:6003933439012269528
。
问题根源
经过反复测试和分析,发现问题出在FastJSON处理大数字时的一个坑:
- 大数字处理问题:当JSON中包含超出JavaScript安全整数范围的数字时,FastJSON在某些情况下会出现解析异常
- 类型转换问题 :直接使用
JSON.toJSON()
方法可能会触发内部的类型推断机制,导致解析错误 - 字符编码问题:在某些环境下,字符编码也可能影响JSON解析
解决方案
方案一:避免使用 JSON.toJSON()
最直接的解决方法就是改变JSON处理方式:
java
// 原来的写法(有问题)
com.alibaba.fastjson2.JSONObject contentJson =
(com.alibaba.fastjson2.JSONObject) com.alibaba.fastjson2.JSON.toJSON(invoke.getData().getContent());
// 改进后的写法
String contentStr = com.seeyon.boot.util.JsonUtils.toJson(invoke.getData().getContent());
com.alibaba.fastjson2.JSONObject contentJson = com.alibaba.fastjson2.JSON.parseObject(contentStr);
方案二:统一使用Hutool工具类
既然FastJSON容易踩坑,不如换个更稳定的工具:
java
// 使用Hutool的JSON工具
cn.hutool.json.JSONObject contentJson = new cn.hutool.json.JSONObject(invoke.getData().getContent());
String edocId = contentJson.getStr("formRecordId");
if (StrUtil.isBlank(edocId)) {
edocId = contentJson.getStr("formRecordCode");
}
方案三:增加异常处理
无论用什么方案,都要加上异常处理,让程序更健壮:
java
private String invokeGetEdocIdByAffairId(Long affairId) {
// ... 前面的代码省略
try {
// 使用更安全的JSON处理方式
String contentStr = JsonUtil.toJsonStr(invoke.getData().getContent());
cn.hutool.json.JSONObject contentJson = JSONUtil.parseObj(contentStr);
String edocId = contentJson.getStr("formRecordId");
if (StrUtil.isBlank(edocId)) {
edocId = contentJson.getStr("formRecordCode");
}
log.info("成功提取edocId: {}", edocId);
return edocId;
} catch (Exception e) {
log.error("解析JSON数据失败,原始数据: {}",
invoke.getData().getContent(), e);
return null;
}
}
为什么选择Hutool?
- 稳定性更好:Hutool的JSON工具经过了大量实际项目的验证,对各种边界情况处理得更好
- API更友好 :
getStr()
方法比getString()
更安全,不会因为类型问题抛异常 - 性能不错:虽然不是最快的,但在大多数场景下性能完全够用
- 维护成本低:API设计简洁,出问题的概率更小
经验总结
- 避免直接使用 JSON.toJSON():这个方法在处理复杂对象时容易出问题
- 大数字要小心:超出JavaScript安全整数范围的数字可能会导致解析异常
- 选择合适的工具:FastJSON虽然性能好,但在某些场景下不如Hutool稳定
- 异常处理很重要:JSON解析一定要加try-catch,并记录详细的错误信息
- 测试要充分:要用各种边界数据进行测试,不能只测试正常情况
结语
这次踩坑让我深刻体会到,选择技术工具不能只看性能,稳定性和易用性同样重要。FastJSON在高并发场景下确实性能不错,但对于业务代码来说,稳定性更重要。
希望这篇文章能帮助到遇到类似问题的朋友。如果你也遇到过类似的JSON解析问题,欢迎在评论区分享你的解决方案!
本文档基于实际项目开发中遇到的问题总结而成,如有疑问欢迎交流讨论。
本文首发于[掘金/CSDN],作者:[听雨],转载请注明出处。