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

FastJson踩坑:@JsonField在反序列化時失效的解決

 更新時間:2021年06月18日 14:06:00   作者:insaneXs  
這篇文章主要介紹了FastJson踩坑:@JsonField在反序列化時失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

問題描述

一個對象(某個字段為枚舉類型,為了不采用默認(rèn)的序列化過程,用@JSONField指定了序列化器和反序列器,過程見舊博文),將其放到JSONArray中再序列化JSONArray對象,用得到的JSON字符串再反序列化時,發(fā)現(xiàn)能夠正常反序列化出JSONArray,而對JSONArray中的某個元素再反序列化成類對象時,出錯。

示例

同樣用舊博文的示例做個簡單測試。

基本對象類Article。

public class Article {
    private String title;
    private String content;
    @JSONField(serializeUsing = AuditStatusCodec.class, deserializeUsing = AuditStatusCodec.class)
    private AuditStatus status;
    public Article(){
    }
    public Article(String title, String content, AuditStatus status){
        this.title = title;
        this.content = content;
        this.status = status;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public AuditStatus getStatus() {
        return status;
    }
    public void setStatus(AuditStatus status) {
        this.status = status;
    }
    @Override
    public String toString() {
        return "Article{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", status=" + status +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o){
            return true;
        }
        if (o == null || getClass() != o.getClass()){
            return false;
        }
        Article article = (Article) o;
        return Objects.equals(title, article.title) &&
                Objects.equals(content, article.content) &&
                status == article.status;
    }
    @Override
    public int hashCode() {
        return Objects.hash(title, content, status);
    }
}

枚舉類型AuditStatus。

public enum AuditStatus {
    /**
     * 審核中
     */
    AUDITING(1),
    /**
     * 通過
     */
    PASSED(2),
    /**
     * 失敗
     */
    FAILED(3);
    private int code;
    AuditStatus(int code){
        this.code = code;
    }
    public int getCode() {
        return code;
    }
    public static AuditStatus convert(int code){
        AuditStatus[] enums = AuditStatus.values();
        for(AuditStatus e : enums){
            if(e.code == code){
                return e;
            }
        }
        return null;
    }
}

以及序列化/反序列化器AuditStatusCodec

public class AuditStatusCodec implements ObjectSerializer, ObjectDeserializer {
    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        Object value = parser.parse();
        return value == null ? (T) value : (T) AuditStatus.convert(TypeUtils.castToInt(value));
    }
    @Override
    public int getFastMatchToken() {
        return 0;
    }
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        serializer.write(((AuditStatus)object).getCode());
    }
}

按照出問題的情況,寫模擬用例:

public class FastJsonTest {
    @Test
    public void deserializeTest(){
        testJSONParse();
        testJSONArrayParse();
    }
    protected static void testJSONParse(){
        System.out.println("**************Start Test JSON Parse");
        Article originalArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING);
        String jsonStr = JSON.toJSONString(originalArticle);
        System.out.println("Serialize to json string: " + jsonStr);
        Article deserializeArticle = JSON.parseObject(jsonStr, Article.class);
        System.out.println("Deserialize to Java Object: " + deserializeArticle + "; and the status is " + deserializeArticle.getStatus());
        //Equals
        Assert.assertTrue(deserializeArticle.getStatus().equals(AuditStatus.AUDITING));
        Assert.assertEquals(originalArticle, deserializeArticle);
    }
    protected static void testJSONArrayParse(){
        System.out.println("**************Start Test JSONArray Parse");
        JSONArray arr = new JSONArray();
        Article originArticle = new Article("Article 1", "This is content", AuditStatus.AUDITING);
        arr.add(originArticle);
        String jsonArrStr = JSON.toJSONString(arr);
        System.out.println("Serialize to json array string: " + jsonArrStr);
        arr = JSON.parseArray(jsonArrStr);
        Article deserializeArticle = arr.getObject(0, Article.class);
        System.out.println("Deserialize to json arr, then to java object: " + deserializeArticle + "; ant the status is " + deserializeArticle.getStatus());
        //Not Equals
        Assert.assertFalse(deserializeArticle.getStatus().equals(AuditStatus.AUDITING));
        Assert.assertNotEquals(originArticle, deserializeArticle);
    }
}

看控制臺輸出的情況:

**************Start Test JSON Parse
Serialize to json string: {"content":"This is content","status":1,"title":"Article 1"}
Deserialize to Java Object: Article{title='Article 1', content='This is content', status=AUDITING}; and the status is AUDITING
**************Start Test JSONArray Parse
Serialize to json array string: [{"content":"This is content","status":1,"title":"Article 1"}]
Deserialize to json arr, then to java object: Article{title='Article 1', content='This is content', status=PASSED}; and the status is PASSED

上述代碼中testJsonParse沒有把類對象放到JSONArray中,可以從結(jié)果中看出序列化和反序列化過程均正常。

而testJSONArrayParse先把類對象放到JSONArray中,在從JSONArray中取出對象反序列化,反序列化的結(jié)果就不正常了。

疑問

為什么JSONObject和JSONArray的反序列化過程得到的結(jié)果不一致?兩者的反序列過程差異在哪?

DEBUG

遇事不決,開始DEBUG。

JSON.parseObject的流程

首先,JSON是一個門面類,提供出一些靜態(tài)的方法供外部使用。比如說parseObject()方法。其內(nèi)部會創(chuàng)建解析器DefaultJSONParser,并將解析委托給解析器執(zhí)行。

DefualtJSONParser在創(chuàng)建時接受輸入,全局配置及特性,相當(dāng)于獲取到了本次解析所有的數(shù)據(jù)。同時DefualtJSONParser的內(nèi)部創(chuàng)建了一些用于解析的組件,例如JSONLexer(用于字符串解析)。解析過程在parseObject中執(zhí)行,parseObject會通過ParseConfig(保存解析配置的一個全局對象)獲取到解析器ObjectDeserializer,并由解析器處理真正的解析過程。

在通過Class獲取ObjectDeserializer時,首先會確定ParserConfig中是否緩存了對應(yīng)的反序列化器,如果不存在,則會新建一個JavaBeanDeserializer(對于一般Java對象而言)。在新建過程中,會解析Class的屬性,并保存在JavaBeanInfo中。 解析器的解析過程就是對比JSON字符串中的KEY和JavaBeanInfo的過程,把對應(yīng)的值反序列化出來(判斷是否有JSONField注解,并根據(jù)注解的屬性處理也在這一步),最終還原對象。

以流程圖表示上述過程:

JSONArray.getObject()

JSONArray.getObject()會先從JSONArray中獲取出Object,然后調(diào)用TypeUtils對Object通過TypeUtils.castToJavaBean()轉(zhuǎn)型。

TypeUtils通過根據(jù)需要轉(zhuǎn)型的類型從ParserConfig中獲取ObjectDeserializer反序列化器,對于普通 Java Bean 而言,是JavaBeanDeserializer。

由于JSONArray中取出的Object實(shí)際上是JSONObject對象,因此會由JavaBeanDeserializer反序列化器的createInstance()方法執(zhí)行反序列化,得到對象。

以流程圖表示上述過程:

deserialize 和 createInstance 的不同

deserialize在反序列化時,會從class上獲取更多的屬性,其中就包括JSONField注解上的信息,而createInstance獲取的信息較少,因此忽略JSONField所帶的信息,導(dǎo)致自定義的反序列化器在反序列化時失效。

疑惑

為什么都是反序列化過程,二者在行為和表現(xiàn)上會有所不同?官方是如何定義deserialize和createInstance的?

上述這些問題還需要查詢更多資料來明確。也希望了解緣由的讀者進(jìn)行告知。

問題的解決方式

解決的辦法不先轉(zhuǎn)換成JSONArray,然后再反序列化對象。而是通過JSON.parseArray直接轉(zhuǎn)成對象的List。

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

相關(guān)文章

  • Java異常處理操作實(shí)例小結(jié)

    Java異常處理操作實(shí)例小結(jié)

    這篇文章主要介紹了Java異常處理操作,結(jié)合實(shí)例形式總結(jié)分析了java異常處理常見操作情況與相關(guān)處理技巧,需要的朋友可以參考下
    2019-07-07
  • java中form以post、get方式提交數(shù)據(jù)中文亂碼問題總結(jié)

    java中form以post、get方式提交數(shù)據(jù)中文亂碼問題總結(jié)

    這篇文章主要介紹了java中form以post、get方式提交數(shù)據(jù)中文亂碼問題總結(jié),需要的朋友可以參考下
    2014-10-10
  • Java實(shí)現(xiàn)單人信息管理程序

    Java實(shí)現(xiàn)單人信息管理程序

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)單人信息管理程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 詳解Maven Docker鏡像使用技巧

    詳解Maven Docker鏡像使用技巧

    這篇文章主要介紹了詳解Maven Docker鏡像使用技巧,Maven是目前最流行的Java項(xiàng)目管理工具之一,提供了強(qiáng)大的包依賴管理和應(yīng)用構(gòu)建功能。本文以Maven為例介紹了Docker在應(yīng)用構(gòu)建中的一些常見技巧。
    2018-06-06
  • Spring Cloud Stream如何實(shí)現(xiàn)服務(wù)之間的通訊

    Spring Cloud Stream如何實(shí)現(xiàn)服務(wù)之間的通訊

    這篇文章主要介紹了Spring Cloud Stream如何實(shí)現(xiàn)服務(wù)之間的通訊,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • mybatis-plus主鍵策略生成失敗的解決

    mybatis-plus主鍵策略生成失敗的解決

    本文主要介紹了mybatis-plus主鍵策略生成失敗的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Spring中實(shí)現(xiàn)的三種異步流式接口方法

    Spring中實(shí)現(xiàn)的三種異步流式接口方法

    在現(xiàn)代Web開發(fā)中,接口超時是一個常見的問題,尤其是在處理耗時操作時,傳統(tǒng)的同步接口在處理長時間任務(wù)時會阻塞請求線程,從而影響系統(tǒng)的響應(yīng)能力,本文將詳細(xì)講解Spring中實(shí)現(xiàn)的三種異步流式接口方法,需要的朋友可以參考下
    2024-10-10
  • java中this與super關(guān)鍵字的使用方法

    java中this與super關(guān)鍵字的使用方法

    這篇文章主要介紹了java中this與super關(guān)鍵字的使用方法的相關(guān)資料,希望通過本文能幫助到大家,讓大家徹底理解應(yīng)用java中this與super,需要的朋友可以參考下
    2017-09-09
  • IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟

    IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟

    這篇文章主要介紹了IDEA設(shè)置JVM運(yùn)行參數(shù)的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Rxjava功能操作符的使用方法詳解

    Rxjava功能操作符的使用方法詳解

    這篇文章主要介紹了Rxjava功能操作符的使用方法詳解,還是比較不錯的,這里分享給大家,供需要的朋友參考。
    2017-11-11

最新評論