您的位置:首頁 > 軟件教程 > 教程 > 深度解析JSON反序列化問題

深度解析JSON反序列化問題

來源:好特整理 | 時間:2024-07-08 11:45:58 | 閱讀:171 |  標(biāo)簽: 問 一個 序列 S   | 分享到:

前言 最近我在做知識星球中的商品秒殺系統(tǒng),昨天遇到了一個詭異的json反序列化問題,感覺挺有意思的,現(xiàn)在拿出來跟大家一起分享一下,希望對你會有所幫助。 案發(fā)現(xiàn)場 我最近在做知識星球中的商品秒殺系統(tǒng),寫了一個filter,獲取用戶請求的header中獲取JWT的token信息。 然后根據(jù)token信息

最近在做知識星球中的商品秒殺系統(tǒng)時,遇到了一個詭異的json反序列化問題。這個問題讓我頗感興趣,因此我決定與大家分享一下。希望這篇文章對你有所幫助。

我最近在做知識星球中的商品秒殺系統(tǒng),寫了一個filter,獲取用戶請求的header中的JWT的token信息。接著根據(jù)token信息,獲取到用戶信息,并將用戶信息設(shè)置到用戶上下文當(dāng)中。這樣接口中的業(yè)務(wù)代碼就能通過用戶上下文獲取到當(dāng)前登錄的用戶信息了。我們的token和用戶信息為了性能考慮都保存到了Redis當(dāng)中。用戶信息是一個json字符串。在用戶登錄接口中,將用戶實體使用fastjson工具轉(zhuǎn)換成了字符串,并保存到了Redis當(dāng)中。然后在filter中,通過一定的key獲取Redis中的字符串,反序列化成用戶實體。使用的同樣是fastjson工具。但在反序列化的過程中,filter拋異常了。

我剛開始以為是json數(shù)據(jù)格式有問題。將json字符串復(fù)制到在線json工具,去掉化之后再格式數(shù)據(jù),發(fā)現(xiàn)json格式?jīng)]有問題。然后寫了一個專門的測試類,將日志中打印的json字符串復(fù)制到j(luò)son變量那里,使用JSON.parseObject方法將json字符串轉(zhuǎn)換成Map對象。執(zhí)行結(jié)果是轉(zhuǎn)換成功了。這讓我有點懵逼了。為什么相同的json字符串,在Test類中能夠正常解析,而在filter當(dāng)中卻不行?

我剛開始以為是json數(shù)據(jù)格式有問題。將json字符串復(fù)制到在線json工具,去掉化之后再格式數(shù)據(jù),發(fā)現(xiàn)json格式?jīng)]有問題。然后寫了一個專門的測試類,將日志中打印的json字符串復(fù)制到j(luò)son變量那里,使用JSON.parseObject方法將json字符串轉(zhuǎn)換成Map對象。執(zhí)行結(jié)果是轉(zhuǎn)換成功了。這讓我有點懵逼了。為什么相同的json字符串,在Test類中能夠正常解析,而在filter當(dāng)中卻不行?

我剛開始以為是json數(shù)據(jù)格式有問題。將json字符串復(fù)制到在線json工具,去掉化之后再格式數(shù)據(jù),發(fā)現(xiàn)json格式?jīng)]有問題。然后寫了一個專門的測試類,將日志中打印的json字符串復(fù)制到j(luò)son變量那里,使用JSON.parseObject方法將json字符串轉(zhuǎn)換成Map對象。執(zhí)行結(jié)果是轉(zhuǎn)換成功了。這讓我有點懵逼了。為什么相同的json字符串,在Test類中能夠正常解析,而在filter當(dāng)中卻不行?

當(dāng)時怕搞錯了,debug了一下filter,發(fā)現(xiàn)獲取到的json數(shù)據(jù),跟Test類中的一模一樣。

帶著一臉的疑惑,我做了下面的測試。莫非是反序列化工具有bug?

我嘗試了一下將json的反序列化工具改成google的gson,代碼如下:

Map map = new Gson().fromJson(userJson, Map.class);運(yùn)行之后,報了一個新的異常:com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 2 path $.這里提示json字符串中包含了:$。而$是特殊字符,password是做了加密處理的,里面包含$和.,這兩種特殊字符。為了快速解決問題,我先將這兩個特字符替換成空字符串。

我又嘗試了一下json的反序列化工具,改成Spring自帶的的jackson工具,代碼如下:ObjectMapper objectMapper = new ObjectMapper();try {Map map = objectMapper.readValue(json, Map.class);} catch (JsonProcessingException e) {e.printStackTrace();}調(diào)整之后,反序列化還是報錯:com.fasterxml.jackson.core.JsonParseException: Unexpected character ('\' (code 92)): was expecting double-quote to start field name。3種反序列化工具都不行,說明應(yīng)該不是fastjson的bug導(dǎo)致的當(dāng)前json字符串,反序列化失敗。到底是什么問題呢?

之前的數(shù)據(jù),我在仔細(xì)看了看。里面是對雙引號,是使用了轉(zhuǎn)義的,具體是這樣做的:\"。莫非還是這個轉(zhuǎn)義的問題?其實我之前已經(jīng)注意到了轉(zhuǎn)義的問題,但使用Test類測試過,沒有問題。當(dāng)時的代碼是這樣的:public class Test {public static void main(String[] args) {String json = "{\"accountNonExpired\":true,\"accountNonLocked\":true,\"authorities\":[{\"authority\":\"admin\"}],\"credentialsNonExpired\":true,\"enabled\":true,\"id\":13,\"password\":\"$2a$10$o3XfeGr0SHStAwLuJRW6y.kE0UTerQfv3SXrAcVLuJ6M3hEsC9RKe\",\"roles\":[\"admin\"],\"username\":\"admin\"}";Map map = JSON.parseObject(json, Map.class);System.out.println(map);}}。里面也包含了一些轉(zhuǎn)義字符。我?guī)е囈辉嚨男膽B(tài),接下來,打算將轉(zhuǎn)義字符去掉?纯丛嫉膉son字符串,解析有沒有問題。怎么去掉轉(zhuǎn)義字符呢?手寫工具類,感覺不太好,可能會寫漏一些特殊字符的場景。我想到了org.apache.commons包下的StringEscapeUtils類,它里面的unescapeJava方法,可以輕松去掉Java代碼中的轉(zhuǎn)義字符。于是,我調(diào)整了一下代碼:json = StringEscapeUtils.unescapeJava(json);JSON.parseObject(json, UserEntity.class);這樣處理之后,發(fā)現(xiàn)反序列化成功了。

這個問題最終發(fā)現(xiàn)還是轉(zhuǎn)義的問題。那么,之前Test類中json字符串,也使用了轉(zhuǎn)義,為什么沒有問題?當(dāng)時的代碼是這樣的:public class Test {public static void main(String[] args) {String json = "{\"accountNonExpired\":true,\"accountNonLocked\":true,\"authorities\":[{\"authority\":\"admin\"}],\"credentialsNonExpired\":true,\"enabled\":true,\"id\":13,\"password\":\"$2a$10$o3XfeGr0SHStAwLuJRW6y.kE0UTerQfv3SXrAcVLuJ6M3hEsC9RKe\",\"roles\":[\"admin\"],\"username\":\"admin\"}";Map map = JSON.parseObject(json, Map.class);System.out.println(map);}}。但在filter中的程序,在讀取到這個json字符串之后,發(fā)現(xiàn)該字符串中包含了\轉(zhuǎn)義符號,程序自動把它變成了\\。調(diào)整一下Test類的main方法,改成三個斜杠的json字符串:public static void main(String[] args) {String json = "{\\\"accountNonExpired\\\":true,\\\"accountNonLocked\\\":true,\\\"authorities\\\":[{\\\"authority\\\":\\\"admin\\\"}],\\\"credentialsNonExpired\\\":true,\\\"enabled\\\":true,\\\"id\\\":13,\\\"password\\\":\\\"$2a$10$o3XfeGr0SHStAwLuJRW6y.kE0UTerQfv3SXrAcVLuJ6M3hEsC9RKe\\\",\\\"roles\\\":[\\\"admin\\\"],\\\"username\\\":\\\"admin\\\"}";Map map = JSON.parseObject(json, Map.class);System.out.println(map);}}。執(zhí)行結(jié)果:Exception in thread "main" com.alibaba.fastjson.JSONException: illegal identifier : \pos 1, line 1, column 2{\"accountNonExpired\":true,\"accountNonLocked\":true,\"authorities\":[{\"authority\":\"admin\"}],\"credentialsNonExpired\":true,\"enabled\":true,\"id\":13,\"password\":\"$2a$10$o3XfeGr0SHStAwLuJRW6y.kE0UTerQfv3SXrAcVLuJ6M3hEsC9RKe\",\"roles\":[\"admin\"],\"username\":\"admin\"},拋出了跟文章最開始一樣的異常。說明其實就是轉(zhuǎn)義的問題。之前,我將項目的日志中的json字符串,復(fù)制到idea的Test的json變量中,當(dāng)時將最外層的雙引號一起復(fù)制過來了,保存的是1個斜杠的數(shù)據(jù)。這個操作把我誤導(dǎo)了。而后面從在線的json工具中,把相同的json字符串,復(fù)制到idea的Test的json變量中,在雙引號當(dāng)中粘貼數(shù)據(jù),保存的卻是3個斜杠的數(shù)據(jù),它會自動轉(zhuǎn)義。讓我意識到了問題。好了,下次如果遇到類似的問題,可以直接使用org.apache.commons包下的StringEscapeUtils類,先去掉轉(zhuǎn)義,再反序列化,這樣可以快速解決問題。此外,這次使用了3種不同的反序列化工具,也看到了其中的一些差異。如果你對日常工作中的一些坑,比較感興趣,可以看看我的技術(shù)專欄《程序員最常見的100個問題》,里面有很多干貨,還是非常值得一看的。

如果這篇文章對您有所幫助,或者有所啟發(fā)的話,幫忙掃描下發(fā)二維碼關(guān)注一下,您的支持是我堅持寫作最大的動力。求一鍵三連:點贊、轉(zhuǎn)發(fā)、在看。關(guān)注公眾號:【蘇三說技術(shù)】,在公眾號中回復(fù):面試、代碼神器、開發(fā)手冊、時間管理有超贊的粉絲福利,另外回復(fù):加群,可以跟很多BAT大廠的前輩交流和學(xué)習(xí)。

小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點或證實其描述。

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]

湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2024 haote.com 好特網(wǎng)