欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

FastJSON字段智能匹配踩坑的解決

 更新時(shí)間:2021年06月18日 11:55:27   作者:祈雨v  
這篇文章主要介紹了FastJSON字段智能匹配踩坑的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

背景

2021年第一天早上,客戶突然投訴說(shuō)系統(tǒng)的一個(gè)功能出了問(wèn)題,緊急排查后發(fā)現(xiàn)后端系統(tǒng)確實(shí)出了bug,原因?yàn)榍岸藗鬏數(shù)腏SON報(bào)文,后端反序列化成JavaBean后部分字段的值丟失了。

查看git提交歷史記錄,前端和后端近期并未對(duì)該功能的接口字段做任何修改,聯(lián)想到上個(gè)版本升級(jí)了后端的FastJSON的版本,懷疑是后端系統(tǒng)對(duì)FastJSON升級(jí)導(dǎo)致的問(wèn)題。

復(fù)現(xiàn)

@Data
static class Label {
 @JSONField(name = "label_id")
 private Integer labelId;
 private String labelName;
}
public static void main(String[] args) {
 String value = "{'labelId': 1,'label_name':'name'}";
 Label label = JSON.parseObject(value, Label.class);
 System.out.println(label);
}

低版本

<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.60</version>
</dependency>

使用低版本FastJSON,如上使用1.2.60版本,示例輸出的結(jié)果如下,即兩個(gè)字段JSON解析映射成功。雖然JavaBean中的字段和JSON中的key并不完全匹配(大小寫(xiě)不匹配以及下劃線匹配),但得益于FastJSON的智能匹配,忽略了大小寫(xiě)和下劃線,依然將JSON映射成功。

Label(labelId=1, labelName=name)

高版本

<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.71</version>
</dependency>

使用高版本FastJSON,如上使用1.2.71,示例輸出結(jié)果如下,字段labelId映射失敗,即高版本FastJSON對(duì)智能匹配規(guī)則做了修改,并且未向前兼容而導(dǎo)致了部分字段映射失敗導(dǎo)致了這次的bug。

Label(labelId=null, labelName=name)

原理

解析高版本FastJSON字段智能匹配失敗的原因,首先要先了解智能匹配的規(guī)則。

低版本

低版本的智能匹配規(guī)則的關(guān)鍵代碼如下,翻譯成人話就是:

1、如果JavaBean字段有@JSONField注解且name不空時(shí),則對(duì)name的值忽略字母大小寫(xiě)和-,_兩個(gè)字符

2、否則取JavaBean的字段名,忽略字母大小寫(xiě)和-,_兩個(gè)字符

3、JSON中的key忽略is開(kāi)頭并忽略剩余字母大小寫(xiě)和-,_兩個(gè)字符

// 對(duì)JSON中沒(méi)有成功映射JavaBean的key做智能匹配
// 1. 忽略key的字母大小寫(xiě)和'-','_'兩個(gè)字符
long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
if (this.smartMatchHashArray == null) {
    long[] hashArray = new long[sortedFieldDeserializers.length];
    for (int i = 0; i < sortedFieldDeserializers.length; i++) {
        // fieldInfo.name優(yōu)先取@JSONField的name字段,其次取JavaBean字段名
        // fieldInfo.name忽略字母大小寫(xiě)和'-','_'兩個(gè)字符嘗試與JSON中的key做智能匹配
        hashArray[i] = TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name);
    }
    Arrays.sort(hashArray);
    this.smartMatchHashArray = hashArray;
}
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
// 2. 如果key以'is'開(kāi)頭,則忽略'is'開(kāi)頭并忽略剩余字母大小寫(xiě)和'-','_'兩個(gè)字符
boolean is = false;
if (pos < 0 && (is = key.startsWith("is"))) {
    smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));
    pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
}

在這里插入圖片描述

高版本

高版本的智能匹配規(guī)則的關(guān)鍵代碼如下,翻譯成人話就是:

1、如果JavaBean字段有@JSONField注解且name不空時(shí),則取name的值

2、否則取JavaBean的字段名,忽略字母大小寫(xiě)和-,_兩個(gè)字符

3、JSON中的key忽略is開(kāi)頭并忽略剩余字母大小寫(xiě)和-,_兩個(gè)字符

if (this.smartMatchHashArray == null) {
    long[] hashArray = new long[sortedFieldDeserializers.length];
    for (int i = 0; i < sortedFieldDeserializers.length; i++) {
        // 1. @JSONField的name不空時(shí)取該值直接與JSON中的key做匹配
        // 2. 取JavaBean字段名忽略字母大小寫(xiě)和'-','_'兩個(gè)字符嘗試與JSON中的key做智能匹配
        hashArray[i] = sortedFieldDeserializers[i].fieldInfo.nameHashCode;
    }
    Arrays.sort(hashArray);
    this.smartMatchHashArray = hashArray;
}
// 對(duì)JSON中沒(méi)有成功映射JavaBean的key做智能匹配
// 1. 直接匹配
long smartKeyHash = TypeUtils.fnv1a_64_extract(key);
int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
// 2. 忽略key的字母大小寫(xiě)和'-','_'兩個(gè)字符
if (pos < 0) {
    long smartKeyHash1 = TypeUtils.fnv1a_64_lower(key);
    pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash1);
}
// 3. 如果key以'is'開(kāi)頭,則忽略'is'開(kāi)頭并忽略剩余字母大小寫(xiě)和'-','_'兩個(gè)字符
boolean is = false;
if (pos < 0 && (is = key.startsWith("is"))) {
    smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));
    pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);
}
// 優(yōu)先取@JSONField的name字段直接與JSON中的key做匹配
// 其次取JavaBean字段名忽略字母大小寫(xiě)和'-','_'兩個(gè)字符嘗試與JSON中的key做智能匹配
private long nameHashCode64(String name, JSONField annotation)
{
    if (annotation != null && annotation.name().length() != 0) {
        return TypeUtils.fnv1a_64_extract(name);
    }
    return TypeUtils.fnv1a_64_lower(name);
}

在這里插入圖片描述

區(qū)別

高版本與低版本的智能匹配規(guī)則差異就是:如果JavaBean字段有@JSONField注解且name不空時(shí),低配版對(duì)name的值會(huì)忽略字母大小寫(xiě)和-,_兩個(gè)字符,而高版本則直接取name的值不會(huì)做忽略操作。

因此示例中加了@JSONField注解的labelId字段才會(huì)因?yàn)镕astJSON版本不同而導(dǎo)致反序列化結(jié)果的不同。

在對(duì)FastJSON的最新幾個(gè)版本挨個(gè)排查后定位出智能匹配規(guī)則發(fā)生修改的版本為1.2.71,所以如果代碼中使用了智能匹配,那么建議謹(jǐn)慎升級(jí)到1.2.71及其更高的版本。

另外這么明顯的未向前兼容的規(guī)則修改,應(yīng)該有很多人會(huì)踩坑。于是去FastJSON的GitHub上查看后果然已經(jīng)有人提出了issues:1.2.71以上版本加了JSONField的字段無(wú)法反序列化。

FastJSON解析數(shù)據(jù),字段數(shù)據(jù)不匹配問(wèn)題

FastJSON中@JSONField注解使用

有個(gè)聯(lián)通的數(shù)據(jù)要解析出來(lái)存入數(shù)據(jù)庫(kù),但是提供過(guò)來(lái)的json數(shù)據(jù)有特殊符號(hào)'.','-',之前想著直接把特殊的字符給替換掉,解析出來(lái)

有一種是可以在實(shí)體類上加注解來(lái)替換轉(zhuǎn)出來(lái)的

fastjson的key是根據(jù)javabean里面的getter和setter方法來(lái)的,不是根據(jù)屬性名的,所以會(huì)出現(xiàn)這個(gè)問(wèn)題,你在屬性的get和set方法上面寫(xiě)上標(biāo)注,說(shuō)明轉(zhuǎn)成什么就行了比如 @JSONField(name=”SOMETHING”)

之前想的是替換到j(luò)son數(shù)據(jù)里面的特殊字符,然后把實(shí)體類的.-都替換掉,這樣就可以創(chuàng)建實(shí)體類對(duì)象了,然后在用fastjson轉(zhuǎn)成對(duì)象

后來(lái)知道有fastjson的注解的@JSONField(name="name.age-12"來(lái)映射上實(shí)體類的)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java使用System.currentTimeMillis()方法計(jì)算程序運(yùn)行時(shí)間的示例代碼

    Java使用System.currentTimeMillis()方法計(jì)算程序運(yùn)行時(shí)間的示例代碼

    System.currentTimeMillis() 方法的返回類型為 long ,表示毫秒為單位的當(dāng)前時(shí)間,文中通過(guò)示例代碼介紹了計(jì)算 String 類型與 StringBuilder 類型拼接字符串的耗時(shí)情況,對(duì)Java計(jì)算程序運(yùn)行時(shí)間相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-03-03
  • springboot 配置文件里部分配置未生效的解決

    springboot 配置文件里部分配置未生效的解決

    springboot 配置文件里部分配置未生效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • SpringBoot注冊(cè)Servlet的三種方法詳解

    SpringBoot注冊(cè)Servlet的三種方法詳解

    這篇文章主要介紹了SpringBoot注冊(cè)Servlet的三種方法詳解,教你如何Spring Boot 注冊(cè) Servlet、Filter、Listener,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-05-05
  • 舉例講解Java設(shè)計(jì)模式中的對(duì)象池模式編程

    舉例講解Java設(shè)計(jì)模式中的對(duì)象池模式編程

    這篇文章主要介紹了Java設(shè)計(jì)模式中的對(duì)象池模式編程示例分享,對(duì)象池模式經(jīng)常在多線程開(kāi)發(fā)時(shí)被用到,需要的朋友可以參考下
    2016-02-02
  • Java16 JDK安裝并設(shè)置環(huán)境變量的方法步驟

    Java16 JDK安裝并設(shè)置環(huán)境變量的方法步驟

    突然想起自己大學(xué)剛接觸java的時(shí)候,要下載JDK和配置環(huán)境變量,那時(shí)候我上網(wǎng)找了很多教學(xué),本文就詳細(xì)的介紹一下Java16 JDK安裝并設(shè)置環(huán)境變量,感興趣的可以了解一下
    2021-09-09
  • java入門(mén)概念個(gè)人理解之package與import淺析

    java入門(mén)概念個(gè)人理解之package與import淺析

    下面小編就為大家?guī)?lái)一篇java入門(mén)概念個(gè)人理解之package與import淺析。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • 詳解Java中包裝類Integer的使用

    詳解Java中包裝類Integer的使用

    今天再帶大家復(fù)習(xí)一下Java的相關(guān)知識(shí),文中對(duì)Java包裝類Integer的使用作了非常詳細(xì)的介紹,對(duì)正在學(xué)習(xí)Java基礎(chǔ)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • spring一個(gè)項(xiàng)目多個(gè)模塊聚合打包問(wèn)題解決方案(最新推薦)

    spring一個(gè)項(xiàng)目多個(gè)模塊聚合打包問(wèn)題解決方案(最新推薦)

    最近遇到個(gè)需求,針對(duì)后端解耦模塊較多的項(xiàng)目,想在云端啟動(dòng)時(shí)簡(jiǎn)潔些只啟動(dòng)一個(gè)jar文件的情景,本文重點(diǎn)給大家介紹spring一個(gè)項(xiàng)目多個(gè)模塊聚合打包問(wèn)題解決方案,感興趣的朋友一起看看吧
    2023-09-09
  • spring boot日志管理配置

    spring boot日志管理配置

    這篇文章主要介紹了spring boot日志管理配置的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 詳解如何有效地處理Java中的多線程

    詳解如何有效地處理Java中的多線程

    在現(xiàn)代軟件開(kāi)發(fā)中,多線程編程已成為提高程序性能和響應(yīng)速度的重要手段,Java提供了豐富的多線程支持,使得在Java中實(shí)現(xiàn)并發(fā)操作變得相對(duì)簡(jiǎn)單,本文將深入探討Java多線程編程的基本概念、常見(jiàn)問(wèn)題和最佳實(shí)踐,需要的朋友可以參考下
    2024-06-06

最新評(píng)論