您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何排查GSON時間問題”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何排查GSON時間問題”吧!
近期有一個老項目在測試環境中頻繁出現了GSON反序列化時間問題,錯誤堆棧如下所示:
Exception in thread "main" com.google.gson.JsonSyntaxException: 2021-05-14 14:59:37 at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:81) at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:66) at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:41) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172) at com.google.gson.Gson.fromJson(Gson.java:795) at com.google.gson.Gson.fromJson(Gson.java:761) at com.google.gson.Gson.fromJson(Gson.java:710) at com.google.gson.Gson.fromJson(Gson.java:682) at com.gson.GsonDate.main(GsonDate.java:17) Caused by: java.text.ParseException: Unparseable date: "2021-05-14 14:59:37" at java.text.DateFormat.parse(DateFormat.java:366) at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:79) ... 9 more
錯誤描述也很詳細,就是GSON在反序列化一段Json串的時候,因為某個時間字符串無法反序列化,導致最終整個Json反序列化失敗;
因為此系統數據量比較大,所有會將半年前的數據歸檔到Hbase中,歸檔的時候會將數據庫中的數據序列化為json格式,然后保存到Hbase中;如果是近期半年的數據會直接查詢數據庫,如果是很早的數據才會查詢Hbase,所以出現的概率比較低;
為了方便分析,直接把Json串拷貝到本地,然后再本地進行重現,再進行問題分析,Json串比較長,這里使用如下Json串代替:
{"date":"2021-05-14 14:59:37"}
準備相關代碼如下所示:
public class GsonDate { public static void main(String[] args) { String json = "{\"date\":\"2021-05-14 14:59:37\"}"; GsonDateBean date = new Gson().fromJson(json, GsonDateBean.class); System.out.println(date); } } @Data class GsonDateBean { private Date date; }
執行的結果是可以反序列成功,并沒有出現上面的錯誤,為了找出原因,這里需要分析一下Gson時間轉換的相關源碼;
Gson時間轉換的源碼還是比較簡單的,DateTypeAdapter
部分代碼如下所示:
private final DateFormat enUsFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US); private final DateFormat localFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT); private final DateFormat iso8601Format = buildIso8601Format(); private static DateFormat buildIso8601Format() { DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); return iso8601Format; } private synchronized Date deserializeToDate(String json) { try { return localFormat.parse(json); } catch (ParseException ignored) { } try { return enUsFormat.parse(json); } catch (ParseException ignored) { } try { return iso8601Format.parse(json); } catch (ParseException e) { throw new JsonSyntaxException(json, e); } }
Gson準備了三個DateFormat
,分別是:localFormat,enUsFormat,iso8601Format;轉換的時候也是按照這個順序進行轉換,哪個能轉換成功就直接返回,以上出現問題說明三種DateFormat
都沒有轉換成功;本地調試可以直接Debug進來,可以發現直接使用localFormat就轉換成功了,并且可以分別查看每個的pattern
;
localFormat:yyyy-M-d H:mm:ss
enUsFormat:MMM d, yyyy h:mm:ss a
iso8601Format:yyyy-MM-dd'T'HH:mm:ss'Z'
以上的日期格式完全符合yyyy-M-d H:mm:ss
格式,所以可以直接轉換成功;可以發現localFormat其實是和本地系統的語言環境有關,所以會出現本地運行結果和服務器運行結果不一致;
可以直接通過代碼設置語言環境,把環境設置為Locale.US
public class GsonDate { public static void main(String[] args) { System.out.println("默認:"+Locale.getDefault()); System.out.println("重置語言環境:Locale.US"); Locale.setDefault(Locale.US); String json = "{\"date\":\"2021-05-14 14:59:37\"}"; GsonDateBean date = new Gson().fromJson(json, GsonDateBean.class); System.out.println(date); } }
運行以上代碼,出現了和服務器一樣的反序列時間問題:
默認:zh_CN 重置語言環境:Locale.US Exception in thread "main" com.google.gson.JsonSyntaxException: 2021-05-14 14:59:37 at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:81)
可以發現我們本地的環境一般都是zh_CN
,對應Locale.CHINA
;
可以直接改變系統語言環境,liunx可以直接在/etc/sysconfig/i18n
中配置:
英文版系統: LANG="en_US.UTF-8" 中文版系統: LANG="zh_CN.UTF-8"
可以查看當前配置的語言環境:
[root@Centos ~]# echo $LANG en_US.UTF-8
可以給Gson設置默認的日志轉換格式:
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); GsonDateBean date = gson.fromJson(json, GsonDateBean.class);
同樣的如果使用其他Json序列化工具,比如fastjson
是否也有這樣的問題那,可以簡單做一個測試:
Locale.setDefault(Locale.US); String json = "{\"date\":\"2021-05-14 14:59:37\"}"; String json2 = "{\"date\":\"2021年05月14日 14:59:37\"}"; JacksonDateBean date = JSON.parseObject(json, JacksonDateBean.class);
結果是不僅yyyy-MM-dd HH:mm:ss
格式能被解析,包含中文的年月日
都可以被解析;如果查看相關源碼可以發現,fastjson
并沒有直接使用DateFormat
去做日期格式轉換,而是實現了ISO 8601標準,并且提供了中國常見日期格式的支持;具體可以直接查看源碼JSONScanner
中的scanISO8601DateIfMatch
方法; 另外一點需要說明的是以上GSON使用的是2.2.2
版本,最新版本2.8.6
版本中同樣提供了對ISO 8601標準
的支持,具體可以查看ISO8601Utils
類。
感謝各位的閱讀,以上就是“如何排查GSON時間問題”的內容了,經過本文的學習后,相信大家對如何排查GSON時間問題這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。