【转】Jackson之多态反序列化(⽗类转不同⼦类)
1.场景描述
JSON作为⼀种轻量级的数据交换格式,其清晰和简洁的结构能够轻松地与Java对象产⽣映射关系。例如,⼀个Coke(可⼝可乐)类的java代码如下:
public class Coke{
String name ="Coke";
int capacity=500;
}
⽤json描述该类:
{
"name":"Coke",
"capacity":500
}
⽽这种映射关系可以通过代码进⾏转换,也就是所谓的json序列化和反序列化。
序列化:是指将Java对象转换成Json⽂件或者Json字符串; 反序列化:是指将Json⽂件或者Json字符串转换成Java对象。
Java代码实现Json的序列化和反序列化并不难,尤其是现在的很多框架简化了很多的过程。下⾯以我常⽤的jackson为例,实现简单的json 序列化和反序列化:
Coke类的定义如下
public class Coke {
public String name;
public int capacity;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getCapacity(){
return capacity;
}
public void setCapacity(int capacity){
this.capacity = capacity;
}
}
下⾯是测试类:
public class JsonTest {
@Test
public void JsonTest()throws IOException {
ObjectMapper mapper =new ObjectMapper();
String jsonStr =" {\n"+
" \"name\":\"Coke\",\n"+
" \"capacity\":500\n"+
"}";
//json deserialization
Coke coke = adValue(jsonStr,Coke.class);
System.out.println(coke.capacity);
//json serialization
Coke coke1 =new Coke();
coke1.setName("BigCoke");
coke1.setCapacity(680);
String serializationJson = mapper.writeValueAsString(coke1);
System.out.println(serializationJson);
}
}
输出结果:
对单个类的序列化和反序列化,只要不是结构过于复杂,其操作还是⽐较简单的。对于此类型的序列
化和反序列在这⾥我就不赘述了。 我们现在要讨论的情况是,假如我们现在要对json对象进⾏反序列化操作,但是我们并不知知道具体的json格式,也就是说我们不知道json有哪些字段。这在实际⽣活中很常见,⽐如在商店中,每件商品都有不同的属性。饮料会有容量属性,⽽马桶,我们⼀般不会去考虑"容量"这种东西吧。那我们⼜该如何去做这种可能性很多的反序列化呢?
问题:我们可以做反序列化,但是我们得知道这个json⽂件或者字符串对应的类,⽽上述的情况没法做到"运⾏前"就知道是什么商品。只有在⽤户付款时(运⾏时),我们才知道这个商品是什么。 分析:我们没法在运⾏前知道需要反序列化的商品是什么,但是我们知道⼀共有哪些商品可以被反序列化。⽽反序列化所需要的类我们也可以在⼯程中根据商品类型直接定义。我们要做的只是在获取到商品时告诉它需要反序列化成哪个对象就OK了。⽽商品类型,我们可以根据商品名来判断。那我们现在需要的就是⼀种可以根据json⽂件或json字符串中某个字段判断出需要反序列化成哪⼀种对象的⽅法。幸运的是,jackson也提供了解决这类问题的⽅案。
2. 多态类型的处理
Jackson⽀持多态类型配置,在进⾏jackson反序列化时,可以根据配置转换成相应的⼦类对象。
其配置主要时通过相关的注解实现的。
@JsonTypeInfo
查看注解定义,其结构如图:
由上图可以看出,这个注解⼀共有4个字段,分别是use,include,property和defaultImpl。下⾯分别对这4个字段进⾏说明。
Id类型的use
这个字段时⽤来指定根据哪种类型的元数据来进⾏序列化和反序列化。可选的值有:
1. JsonTypeInfo.Id.CLASS
2. sonTypeInfo.Id.MINIMAL_CLASS
3. JsonTypeInfo.Id.NAME
4. JsonTypeInfo.Id.CUSTOM
5.
JsonTypeInfo.Id.NONE
这⾥我们选择的是JsonTypeInfo.Id.NAME这个值,它表⽰的是我们的Serde将会使⽤字段名称作为依据。针对上述场景,我们将会根据商品名称来进⾏serde。
As类型的include
这个字段是⽤来指定我们的元信息是如何被包含进去的,可选的值如下:
JsonTypeInfo.As.PROPERTY
JsonTypeInfo.As.EXISTING_PROPERTY
JsonTypeInfo.As.EXTERNAL_PROPERTY
JsonTypeInfo.As.WRAPPER_OBJECT
JsonTypeInfo.As.WRAPPER_ARRAY
这个字段我们选择的是JsonTypeInfo.As.PROPERTY,它所表⽰的意思是包含机制将会使⽤⼀个具体的属性值。
String类型的property
只⽤当use为JsonTypeInfo.Id.CUSTOM,或者include为JsonTypeInfo.As.PROPERTY时才会配置这个值。这个就可以⽤来表⽰具体的依据字段。
下⾯是该注解的使⽤ :
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,property ="productName")
这个表⽰的就是在进⾏反序列化时,我们依据productName这个字段来区分需要转换成的对象。例如,productName="Coke"时,我们就将json反序列化成Cok e对象。
@JsonSubTypes
这个注解是结合上个注解⼀起完成多态反序列化的。上个注解指定了反序列化的标标识,⽽这个注解指定了每个标识对应的⼦类。
注解的结构如下:
由注解的结构图可以看出,这注解只有⼀个字段就是@Type类型的数组。⽽@Type的value就是⼦类,name即为⼦类对应的标识。
下⾯是该注解的使⽤:
@JsonSubTypes(value ={
@JsonSubTypes.Type(value = ClassA.class, name ="A"),
@JsonSubTypes.Type(value = ClassA.class, name ="B")
})
上⾯代码所做的⼯作就是,当检测标识为“A”时就将其反序列化ClassA,为“B”时就反序列化成ClassB。
既然已经知道两个注解的⽤法了,接下来我们就通过⼀个Demo看看他们在我们的代码中该如何发挥作⽤。
3. Demo
场景描述:近⽇,某游戏⼚家出品⼀种新的游戏装备实体卡。玩家购买实体卡通过扫码之后就可以获得相应的道具,这些卡机具收藏价值。⽽每张卡的道具都是通过json来描述的,当玩家扫描后,后台
就会根据这些描述信息把装备卡转换成相应的道具。⽬前已出的装备卡有三种,星空魔杖,代达罗斯之殇和巨⼤瓶饮料。三个装备的描述信息分别如下:
星空魔杖
{
"name":"Star wand",
"length":35,
"price":120,
"effect":["getting greater","getting handsome","getting rich"]
}
代达罗斯之殇
{
"name":"Daedalus",
"weight":"5kg",
"damage":1200,
"roles":["assassinator","soldier"],
"origin":{
"name":"Mainland of warcraft",
"date":"142-12-25"
}
}
巨⼤瓶饮料
{
"name":"Huge drink",
"capacity":500000,
"effect":"quenching your thirst and tasting good"
}
⾸先定义⽗类,⽤于反序列化时指定参数。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,property ="name")
@JsonSubTypes(value ={
@JsonSubTypes.Type(value = Daedalus.class, name ="Daedalus"),
@JsonSubTypes.Type(value = HugeDrink.class, name ="Huge drink"),
@JsonSubTypes.Type(value = StarWand.class, name ="Star wand"),
})
public interface Equipment {
}
这个接⼝的定义很简单,只是为了将各种装备划分成⼀类。然后通过注解指定了其⼦类类型,⼦类的标识字段以及每个⼦类对应的标识值。然后根据描述信息我们可以很轻松地写出这三个类的定义:
public class StarWand implements Equipment{
private String name;
private int length;
private int price;
private List<String> effect;
public void setName(String name){
this.name = name;
}
public void setLength(int length){
this.length = length;
}
public void setPrice(int price){
this.price = price;
}
public void setEffect(List<String> effect){
this.effect = effect;
}
public String getName(){
return name;
}
public int getLength(){
return length;
}
public int getPrice(){
return price;
}
public List<String>getEffect(){
return effect;
}
}
public class Daedalus implements Equipment { private String name;
private String weight;
private int damage;
private List<String> roles;
private Map<String,String> origin;
public void setName(String name){
this.name = name;
}
public void setWeight(String weight){
this.weight = weight;
}
public void setDamage(int damage){
this.damage = damage;
}
public void setRoles(List<String> roles){json值的类型有哪些
}
public void setOrigin(Map<String, String> origin){ igin = origin;
}
public String getName(){
return name;
}
public String getWeight(){
return weight;
}
public int getDamage(){
return damage;
}
public List<String>getRoles(){
return roles;
}
public Map<String, String>getOrigin(){
return origin;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论