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

問題描述

一個對象(某個字段爲枚舉類型,爲了避免採用默認的序列化過程,用@JSONField指定了序列化器和反序列器,過程見舊博文),將其放到JSONArray中再序列化JSONArray對象,用獲得的JSON字符串再反序列化時,發現可以正常反序列化出JSONArray,而對JSONArray中的某個元素再反序列化成類對象時,出錯。html

示例

一樣用舊博文的示例作個簡單測試。
基本對象類Articlejava

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);
    }
}

枚舉類型AuditStatusjson

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());
    }
}

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

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中,能夠從結果中看出序列化和反序列化過程均正常。而testJSONArrayParse先把類對象放到JSONArray中,在從JSONArray中取出對象反序列化,反序列化的結果就不正常了。this

疑問

爲何JSONObjectJSONArray的反序列化過程獲得的結果不一致?二者的反序列過程差別在哪?spa

DEBUG

遇事不決,開始DEBUGcode

JSON.parseObject的流程

首先,JSON是一個門面類,提供出一些靜態的方法供外部使用。好比說parseObject()方法。其內部會建立解析器DefaultJSONParser,並將解析委託給解析器執行。htm

DefualtJSONParser在建立時接受輸入,全局配置及特性,至關於獲取到了本次解析全部的數據。同時DefualtJSONParser的內部建立了一些用於解析的組件,例如JSONLexer(用於字符串解析)。解析過程在parseObject中執行,parseObject會經過ParseConfig(保存解析配置的一個全局對象)獲取到解析器ObjectDeserializer,並由解析器處理真正的解析過程。

在經過Class獲取ObjectDeserializer時,首先會肯定ParserConfig中是否緩存了對應的反序列化器,若是不存在,則會新建一個JavaBeanDeserializer(對於通常Java對象而言)。在新建過程當中,會解析Class的屬性,並保存在JavaBeanInfo中。 解析器的解析過程就是對比JSON字符串中的KEYJavaBeanInfo的過程,把對應的值反序列化出來(判斷是否有JSONField註解,並根據註解的屬性處理也在這一步),最終還原對象。
以流程圖表示上述過程:

JSONArray.getObject()

JSONArray.getObject()會先從JSONArray中獲取出Object,而後調用TypeUtilsObject經過TypeUtils.castToJavaBean()轉型。
TypeUtils經過根據須要轉型的類型從ParserConfig中獲取ObjectDeserializer反序列化器,對於普通 Java Bean 而言,是JavaBeanDeserializer
因爲JSONArray中取出的Object其實是JSONObject對象,所以會由JavaBeanDeserializer反序列化器的createInstance()方法執行反序列化,獲得對象。
以流程圖表示上述過程:

deserialize 和 createInstance 的不一樣

deserialize在反序列化時,會從class上獲取更多的屬性,其中就包括JSONField註解上的信息,而createInstance獲取的信息較少,所以忽略JSONField所帶的信息,致使自定義的反序列化器在反序列化時失效。

疑惑

爲何都是反序列化過程,兩者在行爲和表現上會有所不一樣?官方是如何定義deserializecreateInstance的?
上述這些問題還須要查詢更多資料來明確。也但願瞭解原因的讀者進行告知。

問題的解決方式

解決的辦法不先轉換成JSONArray,而後再反序列化對象。而是經過JSON.parseArray直接轉成對象的List

相關文章
相關標籤/搜索