淺談Gson和fastjson使用中的坑

相信大家在代碼編寫中都用過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ò)哼蛆。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蕊梧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子腮介,更是在濱河造成了極大的恐慌肥矢,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叠洗,死亡現(xiàn)場(chǎng)離奇詭異甘改,居然都是意外死亡旅东,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門十艾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抵代,“玉大人,你說我怎么就攤上這事疟羹≈魇兀” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵榄融,是天一觀的道長参淫。 經(jīng)常有香客問我,道長愧杯,這世上最難降的妖魔是什么涎才? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮力九,結(jié)果婚禮上耍铜,老公的妹妹穿的比我還像新娘。我一直安慰自己跌前,他們只是感情好棕兼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抵乓,像睡著了一般伴挚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灾炭,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天茎芋,我揣著相機(jī)與錄音,去河邊找鬼蜈出。 笑死田弥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铡原。 我是一名探鬼主播偷厦,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼燕刻!你這毒婦竟也來了沪哺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤酌儒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后枯途,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忌怎,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡籍滴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榴啸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孽惰。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鸥印,靈堂內(nèi)的尸體忽然破棺而出勋功,到底是詐尸還是另有隱情,我是刑警寧澤库说,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布狂鞋,位于F島的核電站,受9級(jí)特大地震影響潜的,放射性物質(zhì)發(fā)生泄漏骚揍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一啰挪、第九天 我趴在偏房一處隱蔽的房頂上張望信不。 院中可真熱鬧,春花似錦亡呵、人聲如沸抽活。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽下硕。三九已至,卻和暖如春歇由,著一層夾襖步出監(jiān)牢的瞬間卵牍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工沦泌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糊昙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓谢谦,卻偏偏與公主長得像释牺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子回挽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容