使用FastJSON解析JSON發(fā)生異常的踩坑記錄
前言
最近在開發(fā)電子簽名項(xiàng)目時遇到了一個特別頭疼的問題,系統(tǒng)在處理JSON數(shù)據(jù)時突然報錯,錯誤信息看起來很奇怪:syntax error : f。經(jīng)過一番排查,發(fā)現(xiàn)是FastJSON在處理特定數(shù)據(jù)時的一個坑。今天把這個問題記錄下來,希望能幫到遇到類似問題的朋友。
問題現(xiàn)象
系統(tǒng)運(yùn)行得好好的,突然就報錯了:
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)
看到這個錯誤,第一反應(yīng)是:什么鬼?JSON格式?jīng)]問題啊,怎么就解析不了了?
問題代碼
出問題的代碼很簡單,就是想從返回的數(shù)據(jù)中提取一個字段:
// 這行代碼出問題了
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");
}
數(shù)據(jù)分析
接口返回的JSON數(shù)據(jù)看起來完全正常:
{
"id": 8745074954268373736,
"startAccountId": -8901286526055593580,
"generateType": null,
"prevNodeId": 175688901899730,
"formRecordId": 6003933439012269528,
"pcPageParam": null,
"dealTime": 0,
"nodeName": "管理員",
"formRecordCode": "6003933439012269528",
"extLong1": 0,
"overdueWorkingShow": null
}
乍一看沒什么問題,但仔細(xì)觀察會發(fā)現(xiàn),formRecordId 的值是一個很大的數(shù)字:6003933439012269528。
問題根源
經(jīng)過反復(fù)測試和分析,發(fā)現(xiàn)問題出在FastJSON處理大數(shù)字時的一個坑:
- 大數(shù)字處理問題:當(dāng)JSON中包含超出JavaScript安全整數(shù)范圍的數(shù)字時,F(xiàn)astJSON在某些情況下會出現(xiàn)解析異常
- 類型轉(zhuǎn)換問題:直接使用
JSON.toJSON()方法可能會觸發(fā)內(nèi)部的類型推斷機(jī)制,導(dǎo)致解析錯誤 - 字符編碼問題:在某些環(huán)境下,字符編碼也可能影響JSON解析
解決方案
方案一:避免使用 JSON.toJSON()
最直接的解決方法就是改變JSON處理方式:
// 原來的寫法(有問題)
com.alibaba.fastjson2.JSONObject contentJson =
(com.alibaba.fastjson2.JSONObject) com.alibaba.fastjson2.JSON.toJSON(invoke.getData().getContent());
// 改進(jìn)后的寫法
String contentStr = com.seeyon.boot.util.JsonUtils.toJson(invoke.getData().getContent());
com.alibaba.fastjson2.JSONObject contentJson = com.alibaba.fastjson2.JSON.parseObject(contentStr);
方案二:統(tǒng)一使用Hutool工具類
既然FastJSON容易踩坑,不如換個更穩(wěn)定的工具:
// 使用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");
}
方案三:增加異常處理
無論用什么方案,都要加上異常處理,讓程序更健壯:
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數(shù)據(jù)失敗,原始數(shù)據(jù): {}",
invoke.getData().getContent(), e);
return null;
}
}
為什么選擇Hutool
穩(wěn)定性更好:Hutool的JSON工具經(jīng)過了大量實(shí)際項(xiàng)目的驗(yàn)證,對各種邊界情況處理得更好
API更友好:getStr() 方法比 getString() 更安全,不會因?yàn)轭愋蛦栴}拋異常
性能不錯:雖然不是最快的,但在大多數(shù)場景下性能完全夠用
維護(hù)成本低:API設(shè)計簡潔,出問題的概率更小
經(jīng)驗(yàn)總結(jié)
避免直接使用 JSON.toJSON():這個方法在處理復(fù)雜對象時容易出問題
大數(shù)字要小心:超出JavaScript安全整數(shù)范圍的數(shù)字可能會導(dǎo)致解析異常
選擇合適的工具:FastJSON雖然性能好,但在某些場景下不如Hutool穩(wěn)定
異常處理很重要:JSON解析一定要加try-catch,并記錄詳細(xì)的錯誤信息
測試要充分:要用各種邊界數(shù)據(jù)進(jìn)行測試,不能只測試正常情況
結(jié)語
這次踩坑讓我深刻體會到,選擇技術(shù)工具不能只看性能,穩(wěn)定性和易用性同樣重要。FastJSON在高并發(fā)場景下確實(shí)性能不錯,但對于業(yè)務(wù)代碼來說,穩(wěn)定性更重要。
到此這篇關(guān)于使用FastJSON解析JSON發(fā)生異常的踩坑記錄的文章就介紹到這了,更多相關(guān)FastJSON解析JSON內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決執(zhí)行maven命令時提示Process terminated的問題
這篇文章主要介紹了解決執(zhí)行maven命令時提示Process terminated的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
詳解SpringBoot2.0的@Cacheable(Redis)緩存失效時間解決方案
這篇文章主要介紹了詳解SpringBoot2.0的@Cacheable(Redis)緩存失效時間解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
雪花算法(snowflak)生成有序不重復(fù)ID的Java實(shí)現(xiàn)代碼
雪花算法是一種分布式系統(tǒng)中生成唯一ID的方法,由41位時間戳、10位機(jī)器碼和12位序列號組成,具有唯一性、有序性和高效率等優(yōu)點(diǎn),這篇文章主要介紹了雪花算法(snowflak)生成有序不重復(fù)ID的Java實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-11-11
Spring定時任務(wù)只執(zhí)行一次的原因分析與解決方案
在使用Spring的@Scheduled定時任務(wù)時,你是否遇到過任務(wù)只執(zhí)行一次,后續(xù)不再觸發(fā)的情況?這種情況可能由多種原因?qū)е?如未啟用調(diào)度、線程池問題、異常中斷等,本文將深入分析Spring定時任務(wù)只執(zhí)行一次的原因,并提供完整的解決方案,需要的朋友可以參考下2025-03-03
Java 基礎(chǔ)之NIO 學(xué)習(xí)詳解
這篇文章主要介紹了java基礎(chǔ)之NIO介紹及使用,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-09-09
java使用htmlparser提取網(wǎng)頁純文本例子
這篇文章主要介紹了java使用htmlparser提取網(wǎng)頁純文本例子,需要的朋友可以參考下2014-04-04
SpringBoot 項(xiàng)目使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方
在實(shí)際的開發(fā)過程中一個互聯(lián)網(wǎng)的項(xiàng)目來說 ,有可能會涉及到調(diào)用外部接口的實(shí)際業(yè)務(wù)場景,下面通過本文給大家介紹SpringBoot 項(xiàng)目 使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方法,需要的朋友可以參考下2022-06-06
Java JAR 啟動內(nèi)存參數(shù)配置指南(從基礎(chǔ)設(shè)置到性能優(yōu)化)
在啟動 Java 可執(zhí)行 JAR 文件時,合理配置 JVM 內(nèi)存參數(shù)是保障應(yīng)用穩(wěn)定性和性能的關(guān)鍵,本文將系統(tǒng)講解如何通過命令行參數(shù)、環(huán)境變量等方式指定內(nèi)存配置,并結(jié)合實(shí)際場景提供優(yōu)化建議,感興趣的朋友跟隨小編一起看看吧2025-11-11

