前言
博主github
博主個人博客http://blog.healerjean.com
Jackson允許配置多態類型處理,當進行反序列話時,Json數據匹配的對象可能有多個子類型,爲了正確的讀取對象的類型,我們需要添加一些類型信息。
1、解釋
1.1、@JsonTypeInfo
@JsonTypeInfo
這個註解可以直接放在類上,也可以放在某個屬性上:下面是內部的屬性值
1.1.1、use: (必選):
use: (必選):定義使用哪一種類型識別碼(property爲識別碼的key),可選值有多種:在序列化時標誌出不同的類型用什麼區分,用在反序列化時轉換成響應的類型
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.2、include (可選)
設置識別碼包含在哪裏。 包含類型元數據的一種機制
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.3、property 可選)
設置識別碼是名稱, 自定義的區分類型的id,根據 use的屬性值不同,默認值不同,具體默認值看(1.1.1)
1.1.4、visible (可選)
visible(可選):定義識別碼在反序列化時是否保留(不管false或true都不影響序列化)。默認是false,表示Jackson可以將識別碼識別爲類型後就刪除。
1.2、@JsonSubTypes
可以用來表明這個父類的子類型有哪些
2、準備
2.1、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.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、開始
3.1、實體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.2、測試用例
@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.3、Use (必選)
3.1.3.1、use = JsonTypeInfo. Id.NAME
使用JsonTypeInfo的值作爲識別碼的值,如果有多個子類的情況,必須有 @JsonSubTypes,否則無法判斷是哪個子類
@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
** -
根據Json數據,識別之後,其值爲
@JsonSubTypes.Type(value = Human.Man.class, name = "man")
中子類Man的name值
1、配置 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.2、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數據,識別之後,值爲,這個子類所在類路徑,用.連接
1、這個時候發現和@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.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.4、include (可選)
默認是 JsonTypeInfo.As.PROPERTY,上面所有的都是以它進行測試的
3.1.4.1、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.2、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.3、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.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.5、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.6、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 】