Jackson多態處理

前言

博主github

博主我的博客http://blog.healerjean.com

Jackson容許配置多態類型處理,當進行反序列話時,Json數據匹配的對象可能有多個子類型,爲了正確的讀取對象的類型,咱們須要添加一些類型信息。java

一、解釋

1.一、@JsonTypeInfo

@JsonTypeInfo這個註解能夠直接放在類上,也能夠放在某個屬性上:下面是內部的屬性值git

1.1.一、use: (必選):

use: (必選):定義使用哪種類型識別碼(property爲識別碼的key),可選值有多種:在序列化時標誌出不一樣的類型用什麼區分,用在反序列化時轉換成響應的類型github

use屬性值 若不指定property則默認 做用 是否依賴JsonTypeInfo的值
JsonTypeInfo.Id.NAME @type 使用JsonTypeInfo的值做爲識別碼的值 若是有多個子類的狀況,必須有 @JsonSubTypes,不然沒法判斷是哪一個子類
JsonTypeInfo.Id.CLASS @class 用類的全路勁名稱來做爲識別碼的值 與是否有@JsonSubTypes無關
JsonTypeInfo.Id.MINIMAL_CLASS @c 表示具備最小路徑的Java類名稱用做識別 是否有@JsonSubTypes無關
JsonTypeInfo.Id.NONE 暫不介紹
JsonTypeInfo.Id.CUSTOM 暫不介紹

1.1.二、include (可選)

設置識別碼包含在哪裏。 包含類型元數據的一種機制web

include屬性值 做用
JsonTypeInfo.As.PROPERTY 做爲POJO的屬性出現 默認
JsonTypeInfo.As.WRAPPER_OBJECT 做爲一個包裝的對象
JsonTypeInfo.As.WRAPPER_ARRAY 做爲一個包裝的數組
JsonTypeInfo.As.EXTERNAL_PROPERTY 做爲一個額外的屬性,跟POJO同級,只能用於屬性,如何做用於類則跟JsonTypeInfo.As.PROPERTY是相同效果
JsonTypeInfo.As.EXISTING_PROPERTY 序列化,則Jackson不主動處理,由咱們自行處理。 反序列化的時候,跟JsonTypeInfo.As.PROPERTY的處理相同;

1.1.三、property 可選)

設置識別碼是名稱, 自定義的區分類型的id,根據 use的屬性值不一樣,默認值不一樣,具體默認值看(1.1.1)json

1.1.四、visible (可選)

visible(可選):定義識別碼在反序列化時是否保留(無論false或true都不影響序列化)。默認是false,表示Jackson能夠將識別碼識別爲類型後就刪除。數組

1.二、@JsonSubTypes

能夠用來代表這個父類的子類型有哪些微信

二、準備

2.一、Demo實體類

public abstract class Human {

    private String district;

    @Data
    public static class Man extends Human {
        private String manField;
    }

    @Data
    public static class Woman extends Human {
        private String womanField;
    }

}

2.二、正常狀況下的錯誤的演示

@Test
    public void normal() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Man man = new Man();
        man.setManField("男人");
        man.setDistrict("山西");
        String json = mapper.writeValueAsString(man);
        System.out.println(json);
        // {"district":"山西","manField":"男人"}
        //報錯 子類轉父類,再不能直接序列化爲子類
        man =((Man)mapper.readValue(json, Human.class)) ;
    }

三、開始

3.一、實體demo

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})
public abstract class Human {

    private String district;

    @Data
    public static class Man extends Human {
        private String manField;
    }

    @Data
    public static class Woman extends Human {
        private String womanField;
    }

}

3.二、測試用例

@Test
    public void testOne() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Man man = new Man();
        man.setManField("男人");
        man.setDistrict("北京");

        String manJson = mapper.writeValueAsString(man);
        log.info("序列化Man :【 {} 】", manJson);
        Human human = mapper.readValue(manJson, Human.class);
        log.info("子類轉父類 ======================");
        String humanJson = mapper.writeValueAsString(human);
        log.info("反序列化man -> Human :【 {} 】", humanJson);
        log.info("human.getDistrict() :【 {} 】",  human.getDistrict());
}

3.1.三、Use (必選)

3.1.3.一、use = JsonTypeInfo. Id.NAME

使用JsonTypeInfo的值做爲識別碼的值,若是有多個子類的狀況,必須有 @JsonSubTypes,不然沒法判斷是哪一個子類app

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})


序列化Man :【 {"@type":"man","district":"北京","manField":"男人"} 】
子類轉父類 ======================
反序列化man -> Human :【 {"@type":"man","district":"北京","manField":"男人"} 】
human.getDistrict()  :【 北京 】
  • **use = JsonTypeInfo.Id.NAME, 若是不寫property ,則識別碼 的名字默認爲 @type **svg

  • 根據Json數據,識別以後,其值爲@JsonSubTypes.Type(value = Human.Man.class, name = "man")中子類Man的name值測試

一、配置 property = 「type」
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME
			  property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})

序列化Man :【 {"type":"man","district":"北京","manField":"男人"} 】 
子類轉父類 ======================
反序列化man -> Human :【 {"type":"man","district":"北京","manField":"男人"} 】
human.getDistrict()  :【 北京 】
  • 這個時候默認"@type就變成了type,若是換成其餘的use,則默認值失效,變成這裏的property 的值

3.1.3.二、use = JsonTypeInfo. Id.CLASS、property = 「type」

**用類的全路勁名稱來做爲識別碼的值,和@JsonSubType不要緊,寫上了也不起做用,能夠自動判斷有哪些子類 **

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,
        	  property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})

序列化Man :【 {"type":"com.healerjean.proj.a_test.json.jackson.d02_JsonType.Human$Man","district":"北京","manField":"男人"} 】 
反序列化man -> Human :【 {"type":"com.healerjean.proj.a_test.json.jackson.d02_JsonType.Human$Man","district":"北京","manField":"男人"} 】 
human.getDistrict()  :【 北京 】
  • **use = JsonTypeInfo.Id.CLASS, 若是不寫property ,則識別碼 的名字默認爲 @class **

  • 根據Json數據,識別以後,值爲,這個子類所在類路徑,用.鏈接

一、這個時候發現和@JsonSubTypes中的值彷佛沒有什麼關係,我果斷去掉@JsonSubTypes屬性
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)



序列化Man :【 {"@class` ":"com.healerjean.proj.a_test.json.jackson.d02_JsonType.Human$Man","district":"北京","manField":"男人"} 】 
反序列化man -> Human :【 {"@class` ":"com.healerjean.proj.a_test.json.jackson.d02_JsonType.Human$Man","district":"北京","manField":"男人"} 】 
human.getDistrict()  :【 北京 】

3.1.3.三、use = JsonTypeInfo. Id.MINIMAL_CLASS

**表示具備最小路徑的Java類名稱用做識別碼的值,和@JsonSubType不要緊,寫上了也不起做用,能夠自動判斷有哪些子類 **

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)


序列化Man :【 {"@c":".Human$Man","district":"北京","manField":"男人"} 】
子類轉父類 ====================== 
反序列化man -> Human :【 {"@c":".Human$Man","district":"北京","manField":"男人"} 】human.getDistrict()  :【 北京 】
  • **use = JsonTypeInfo.Id.CLASS, 若是不寫property ,則識別碼 的名字默認爲 @c **

3.1.四、include (可選)

默認是 JsonTypeInfo.As.PROPERTY,上面全部的都是以它進行測試的

3.1.4.一、include = JsonTypeInfo.As.WRAPPER_OBJECT

做爲一個包裝的對象

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.WRAPPER_OBJECT,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})


序列化Man :【 {"man":{"district":"北京","manField":"男人"}} 】
子類轉父類 ====================== 
反序列化man -> Human :【 {"man":{"district":"北京","manField":"男人"}} 】
human.getDistrict()  :【 北京 】

3.1.4.二、include = JsonTypeInfo.As.WRAPPER_ARRAY

做爲一個包裝的數組看下面,這個數組但是不太規則哦,就是說裏面放的不必定的同類型的),和有無property無關,看下面的測試打印結果就知道

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.WRAPPER_ARRAY,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})


序列化Man :【 ["man",{"district":"北京","manField":"男人"}] 】
子類轉父類 ====================== 
反序列化man -> Human :【 ["man",{"district":"北京","manField":"男人"}] 】
human.getDistrict()  :【 北京 】

3.1.4.三、include = JsonTypeInfo.As.EXTERNAL_PROPERTY

做爲一個額外的屬性,跟POJO同級,只能用於屬性,如何做用於類則跟JsonTypeInfo.As.PROPERTY是相同效果

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})


序列化Man :【 {"type":"man","district":"北京","manField":"男人"} 】
類轉父類 ======================
反序列化man -> Human :【 {"type":"man","district":"北京","manField":"男人"} 】
human.getDistrict()  :【 北京 】

3.1.4.四、include = JsonTypeInfo.As.EXISTING_PROPERTY

反序列化 :

  • 這個屬性但是騷的厲害了,它要求property識別碼的名稱必須在被該屬性表示的類中也存在一份,不然反序列化就會報錯。
  • 反序列化的時候這個識別碼名字屬性被認爲是識別碼,若是這個識別碼的值,不在@JsonSubTypes 的name中就會報錯
  • 再者,反序列化以後這個屬性值就不存在了,字段值也不存在了,除非再給對象從新賦值,這個值必須和咱們真正意義上的反序列化的子類的JsonSubTypes 中 name同樣,不能夠任意挑選,不然會報錯

序列化 :

  • 跟它沒半毛錢關係,和整個註解也不要緊

序列化的時候,跟它沒半毛錢關係,可是不會像上面的那樣

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})
public class Human {

    private String district;

    private String type;

    @Data
    public static class Man extends Human {
        private String manField;
    }

    @Data
    public static class Woman extends Human {
        private String womanField;
    }

}




序列化Man :【 {"district":"北京","type":"man","manField":"男人"} 】
子類轉父類 ======================
 反序列化man -> Human :【 {"district":"北京","type":null,"manField":"男人"} 】
 human.getDistrict()  :【 北京 】

2.1.五、property (可選)

設置識別碼是名稱,根據use的屬性,通常狀況下有默認值

除了JsonTypeInfo.As.EXISTING_PROPERTY 有些特殊以外,JsonTypeInfo.As.EXTERNAL_PROPERTY、JsonTypeInfo.As.PROPERTY 註解的類中有了相同的屬性(剩下的不講解,由於其餘的和property 沒半毛錢關係),則會出現兩次 以JsonTypeInfo.As.PROPERTY 舉例

注意點

  • 在visible 爲默認值 false的狀況下反序列化, 雖然會出現兩次,可是在位置上有嚴格要求,我測試過好屢次,發現這個標識碼確定是靠前的,並且不能刪除,刪除就錯了(固然啊,刪除了,怎麼判斷你是哪一個子類)
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})
public class Human {

    private String district;

    private String type;

    @Data
    public static class Man extends Human {
        private String manField;
    }

    @Data
    public static class Woman extends Human {
        private String womanField;
    }

}



   @Test
    public void testOne() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Man man = new Man();
        man.setManField("男人");
        man.setDistrict("北京");
        man.setType("lalala");

        String manJson = mapper.writeValueAsString(man);
        log.info("序列化Man :【 {} 】", manJson);
        Human human = mapper.readValue(manJson, Human.class);
        log.info("子類轉父類 ======================");
        String humanJson = mapper.writeValueAsString(human);
        log.info("反序列化man -> Human :【 {} 】", humanJson);
        log.info("human.getDistrict() :【 {} 】",  human.getDistrict());
        log.info("human.getType() :【 {} 】",  human.getType());
    }



序列化Man :【 {"type":"man","district":"北京","type":"lalala","manField":"男人"} 
子類轉父類 ======================
反序列化man -> Human :【 {"type":"man","district":"北京","type":"lalala","manField":"男人"} 】 
human.getDistrict()  :【 北京 】
 human.getType()  :【 lalala 】

2.1.六、visible(可選)

visible(可選):定義識別碼在反序列化(反序列爲對象內部是否保留給字段)時是否保留(無論false或true都不影響序列化)。默認是false,表示Jackson能夠將識別碼識別爲類型後就刪除。

@Test
    public void testOne() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Man man = new Man();
        man.setManField("男人");
        man.setDistrict("北京");
        man.setType("1");

        String manJson = mapper.writeValueAsString(man);
        log.info("序列化Man :【 {} 】", manJson);
        //只剩下一個type,必須放在第一個,這樣才能識別子類
        manJson = "{\"type\":\"man\",\"district\":\"北京\",\"manField\":\"男人\"} ";
        Human human = mapper.readValue(manJson, Human.class);
        log.info("子類轉父類 ======================");
        String humanJson = mapper.writeValueAsString(human);
        log.info("反序列化man -> Human :【 {} 】", humanJson);
        log.info("human.getDistrict() :【 {} 】",  human.getDistrict());
        log.info("human.getType() :【 {} 】",  human.getType());
    }
  • 通常狀況下 visible = false
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type",
        visible = false
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Human.Man.class, name = "man"),
        @JsonSubTypes.Type(value = Human.Woman.class, name = "woman"),
})


序列化Man :【 {"type":"man","district":"北京","type":"1","manField":"男人"} 】
子類轉父類 ====================== 
反序列化man -> Human :【 {"type":"man","district":"北京","type":null,"manField":"男人"} 】
human.getDistrict()  :【 北京 】
human.getType()  :【 null 】
  • visible = true

反序列化會把它當作一個屬性值處理(保留給字段了),也就是說反序列化後仍是可見的,若是在這個上面我測試的Json中再加一個type,那麼這個這個識別碼照樣能打印,可是會失效

序列化Man :【 {"type":"man","district":"北京","type":"1","manField":"男人"} 】
子類轉父類 ======================
反序列化man -> Human :【 {"type":"man","district":"北京","type":"man","manField":"男人"} 】
 human.getDistrict()  :【 北京 】
human.getType()  :【 man 】



若是在這個上面我測試的Json中再加一個type,那麼這個這個識別碼照樣能打印,可是會失效
manJson = "{\"type\":\"man\",\"district\":\"北京\",\"type\":\"1\",\"manField\":\"男人\"} ";

打印結果
反序列化man -> Human :【 {"type":"man","district":"北京","type":"1","manField":"男人"} 】
human.getType()  :【 1 】
感興趣的,歡迎添加博主微信

哈,博主很樂意和各路好友交流,若是滿意,請打賞博主任意金額,感興趣的在微信轉帳的時候,備註您的微信或者其餘聯繫方式。添加博主微信哦。

請下方留言吧。可與博主自由討論哦

微信 微信公衆號 支付寶
微信 微信公衆號 支付寶
相關文章
相關標籤/搜索