相信大家在代碼編寫中都用過Gson和fastjson吧,用來進(jìn)行 Java對(duì)象和json字符串之間的轉(zhuǎn)換。
本篇文章就主要介紹博主在工作中使用這兩款工具時(shí)遇到的坑和對(duì)應(yīng)的解決辦法。
覺得有用的可以點(diǎn)個(gè)贊哈~
1.前言
看了下我上一篇文章的發(fā)布時(shí)間风罩,已然是兩個(gè)月前了凌箕,這兩個(gè)月工作確實(shí)很忙,加班也不少隐岛,空閑的時(shí)間都去研究別的技術(shù)了(后面會(huì)寫文章),這次先用此篇文章記錄我這段時(shí)間工作中遇到的坑瓷翻,其實(shí)寫文章的主要目的也是想記錄一下問題聚凹,便于幫助他人也便于自己以后查看,廢話不多說齐帚,開始說明問題~
2.Gson和fastjson介紹
Google Gson是一個(gè)簡單的基于Java的庫妒牙,用于將Java對(duì)象序列化為JSON,反之亦然对妄。 它是由Google開發(fā)的一個(gè)開源庫湘今。
fastjson和Gson一樣的作用,但其是由國內(nèi)阿里巴巴開發(fā)的一款工具饥伊。
fastjson比Gson快很多倍象浑,也由于其是國內(nèi)阿里開發(fā)蔫饰,被大量使用,但也頻繁被爆出問題愉豺,官方也緊急修復(fù)更新版本了篓吁。但是實(shí)際使用中,這兩款工具也各有各的坑蚪拦,合理的根據(jù)自己需要的去選擇工具即可杖剪。
3.問題說明及解決
下面將先說明我遇到的坑,然后會(huì)給出相應(yīng)解決辦法驰贷,當(dāng)然大家可以摸索更好的解決方式~
3.1 Gson會(huì)將Integer類型轉(zhuǎn)為Double
場(chǎng)景:一個(gè)業(yè)務(wù)需求的編輯功能盛嘿,我需要把查詢后得到的id隱藏在頁面上,以便保存時(shí)根據(jù)id去修改數(shù)據(jù)括袒。但自測(cè)時(shí)發(fā)現(xiàn)傳到頁面上的值是double類型次兆,但數(shù)據(jù)庫中是int,這就造成了無法修改的問題锹锰,一一排查問題后我發(fā)現(xiàn)是Gson搞的鬼
解決辦法1:使用fastjson即可解決芥炭,引入依賴后代碼使用如下(推薦):
Map<String,Object> map = JSONObject.parseObject(result,Map.class);
解決方法2:將對(duì)象中的Integer類型改成String類型,這樣就不會(huì)被自動(dòng)轉(zhuǎn)換了(根據(jù)自己業(yè)務(wù)情況使用)
解決方法3:在定義Gson時(shí)直接定義類型恃慧,代碼如下:
private static Type typeToken = new TypeToken<TreeMap<String, Object>>(){}.getType();
Gson gson = new GsonBuilder()
.registerTypeAdapter(
new TypeToken<TreeMap<String, Object>>(){}.getType(),
new JsonDeserializer<TreeMap<String, Object>>() {
@Override
public TreeMap<String, Object> deserialize(
JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
TreeMap<String, Object> treeMap = new TreeMap<>();
JsonObject jsonObject = json.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
for (Map.Entry<String, JsonElement> entry : entrySet) {
Object ot = entry.getValue();
if(ot instanceof JsonPrimitive){
treeMap.put(entry.getKey(), ((JsonPrimitive) ot).getAsString());
}else{
treeMap.put(entry.getKey(), ot);
}
}
return treeMap;
}
}).create();
實(shí)際代碼中調(diào)用:
Map<String, Object> params = gson.fromJson(result, typeToken);
3.2 Gson不序列化匿名內(nèi)部類
場(chǎng)景:完成任務(wù)接口時(shí)寫了測(cè)試類來測(cè)接口园蝠,初始化Map集合的時(shí)候用了如下比較優(yōu)雅的方式:
Map<String, String> map = new HashMap<String, String>() {
{
put("logNo", "123456");
put("reqTime", "20210607");
}
};
Gson gson = new Gson();
System.out.println(gson.toJson(map));
但是運(yùn)行測(cè)試類輸出結(jié)果以后會(huì)發(fā)現(xiàn)為null。這是因?yàn)榇朔N方式生成的Map是匿名內(nèi)部類的實(shí)例痢士,也就是new出來的map沒有類名彪薛。
查詢相關(guān)文章可知:內(nèi)部類的適配器會(huì)生成對(duì)外部類/實(shí)例的隱式引用,會(huì)導(dǎo)致循環(huán)引用怠蹂。所以結(jié)論為:Gson不會(huì)序列化匿名內(nèi)部類善延。
解決方法:使用fastjson就不會(huì)出現(xiàn)這種問題,可成功序列化轉(zhuǎn)為json數(shù)據(jù)
看到這里是否覺得Gson出現(xiàn)的問題城侧,fastjson都能解決挚冤,那下面就介紹下fastjson在實(shí)際應(yīng)用中的坑。
3.3 fastjson會(huì)將數(shù)據(jù)順序改變
場(chǎng)景:在對(duì)接其他公司接口時(shí)往往都需要加密解密并簽名驗(yàn)簽赞庶,為了防止傳輸過程中數(shù)據(jù)被篡改,主要是為了安全澳骤。此時(shí)json傳輸?shù)臄?shù)據(jù)順序則是重要的歧强,如果被打亂則會(huì)解密驗(yàn)簽失敗,我在用fastjson時(shí)就出現(xiàn)了這種問題为肮,一度頭疼~
解決方法1:解析返回?cái)?shù)據(jù)時(shí)增加參數(shù)不調(diào)整順序(推薦)
此時(shí)你的fastjson版本應(yīng)該為1.2.3以上
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.3</version>
</dependency>
然后在解析數(shù)據(jù)時(shí)增加 Feature.OrderedField 參數(shù)即可
FrontJsonResponse frontJsonResponse = JSONObject.parseObject(response,FrontJsonResponse.class, Feature.OrderedField);
解決方法2:使用Gson解析數(shù)據(jù)即可摊册,不會(huì)改變數(shù)據(jù)順序
Gson gson = new Gson();
FrontJsonResponse frontJsonResponse = gson.fromJson(response,FrontJsonResponse.class)
3.4 fastjson會(huì)將字段首字母變?yōu)樾?/h3>
場(chǎng)景:在對(duì)接其他公司接口時(shí)颊艳,文檔中字段名稱多為大寫字母(如:FILE_NM等)如果自己想定義對(duì)象實(shí)體來序列化的話茅特,使用fastjson就會(huì)在上送時(shí)將實(shí)體中每個(gè)屬性的首字母變?yōu)樾懀ㄈ纾篺ILE_NM)忘分,這樣上送則肯定會(huì)出問題,可以用以下方法解決:
解決方法1:在屬性名加上 @JSONField(name = "") 注解即可(推薦)
@JSONField(name = "FILE_NM")
private String FILE_NM;
解決方法2:序列化時(shí)增加參數(shù) new PascalNameFilter() 白修,會(huì)將首字母強(qiáng)制轉(zhuǎn)換為大寫妒峦。
JSON.toJSONString(bean,new PascalNameFilter());
3.5 fastjson不會(huì)將多層json轉(zhuǎn)換為Map
場(chǎng)景:在對(duì)接第三方公司接口驗(yàn)簽時(shí),使用fastjson將返回?cái)?shù)據(jù)轉(zhuǎn)換為Map兵睛,再去生成待簽名串驗(yàn)簽時(shí)發(fā)現(xiàn)驗(yàn)簽失敗肯骇,查詢后得知和第三方簽名是生成的串不一致,因?yàn)樘幚矸绞讲煌婧埽唧w場(chǎng)景請(qǐng)看下面示例:
public static void main(String[] args) {
String str = "{\"result\":{\"user_info\":{\"id\":14390753,\"sex\":1},\"complete_status\":0},\"errcode\":0}";
Map<String,Object> fastMap = JSONObject.parseObject(str, Map.class);
System.out.println("fastMap====="+fastMap);
Gson gson = new Gson();
Map<String,Object> gsonMap = gson.fromJson(str,Map.class);
System.out.println("gsonMap====="+gsonMap);
}
以上代碼輸出結(jié)果為:
fastMap====={result={"user_info":{"sex":1,"id":14390753},"complete_status":0}, errcode=0}
gsonMap====={result={user_info={id=1.4390753E7, sex=1.0}, complete_status=0.0}, errcode=0.0}
對(duì)比處理方式和實(shí)際結(jié)果就可以知道笛丙,如果json字符串里有多層json對(duì)象,使用fastjson時(shí)并不會(huì)處理里層的json假颇,只會(huì)將外層json數(shù)據(jù)轉(zhuǎn)換為Map胚鸯,而使用Gson時(shí)會(huì)將符合json格式的數(shù)據(jù)都轉(zhuǎn)換為Map。
最后跟第三方確認(rèn)以后笨鸡,因?yàn)樗麄儾煌涌谟胁煌幚矸绞浇覀冞@邊只能兼容(因?yàn)樗麄兏牟涣耍掠绊憚e的使用方)镜豹,根據(jù)不同接口去判斷該用fastjson還是用Gson解析返回的數(shù)據(jù)再進(jìn)行驗(yàn)簽(真的...一言難盡...)
所以遇到這種情況時(shí)傲须,要確定對(duì)方是以什么方式來處理,我們這邊再確定用什么方式來接趟脂,這也是這兩種工具的一個(gè)不同點(diǎn)泰讽。
4.總結(jié)
實(shí)際開發(fā)過程中遇到以上問題就很頭疼,如果不一步一步debug看代碼輸出結(jié)果昔期,根本不會(huì)想到是json轉(zhuǎn)換工具的問題已卸,但兩款工具確實(shí)各有各的優(yōu)勢(shì),我覺得選擇適合自己業(yè)務(wù)代碼的就是最好的硼一,這些問題也都能解決累澡,也不是用不了,還是看解決方法而已般贼,遇到的這些問題我都會(huì)記錄下來愧哟,等以后碰到類似問題了就能直接解決,這樣也不錯(cuò)哼蛆。