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 {@Testpublic void JsonTest() throws IOException {ObjectMapper mapper = new ObjectMapper();String jsonStr = " {\n" +"      \"name\":\"Coke\",\n" +"      \"capacity\":500\n" +"}";//json deserializationCoke coke = mapper.readValue(jsonStr,Coke.class);System.out.println(coke.capacity);//json serializationCoke 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也提供了解决这类问题的方案。

  1. 多态类型的处理
    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反序列化成Coke对象。

@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看看他们在我们的代码中该如何发挥作用。

  1. 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) {this.roles = roles;}public void setOrigin(Map<String, String> origin) {this.origin = 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;}
}
public class HugeDrink implements Equipment{private String name;private int capacity;private String effect;public void setName(String name) {this.name = name;}public void setCapacity(int capacity) {this.capacity = capacity;}public void setEffect(String effect) {this.effect = effect;}public String getName() {return name;}public int getCapacity() {return capacity;}public String getEffect() {return effect;}
}

最后是主方法

public class Main {public static void main(String[] args) throws IOException {String starWandStr = "{\n" +"    \"name\":\"Star wand\" ,\n" +"    \"length\":35,\n" +"    \"price\":120,\n" +"    \"effect\":[\"getting greater\", \"getting handsome\",\"getting rich\"]\n" +"}";String daedalusStr = "{\n" +"    \"name\":\"Daedalus\",\n" +"    \"weight\":\"5kg\",\n" +"    \"damage\":1200,\n" +"    \"roles\":[\"assassinator\",\"soldier\"],\n" +"    \"origin\":{\n" +"             \"name\":\"Mainland of warcraft\",\n" +"             \"date\":\"142-12-25\"\n" +"    }\n" +"}";String hugeDrinkStr = "{\n" +"    \"name\":\"Huge drink\",\n" +"    \"capacity\":500000,\n" +"    \"effect\":\"quenching your thirst and tasting good\"\n" +"}";ObjectMapper mapper = new ObjectMapper();StarWand starWand = (StarWand)mapper.readValue(starWandStr, Equipment.class);Daedalus daedalus = (Daedalus)mapper.readValue(daedalusStr, Equipment.class);HugeDrink hugeDrink = (HugeDrink)mapper.readValue(hugeDrinkStr, Equipment.class);System.out.println("大佬!您已获得星空魔杖!属性增幅:"+ starWand.getEffect().toString()+"!");System.out.println("大佬!您已获得代达罗斯之殇,增加了 " + daedalus.getDamage() + " 点输出!");System.out.println("大佬!您已获得代达巨大瓶饮料,it "+ hugeDrink.getEffect()+"!");}
}

控制台输出结果如下:

后记
首先需要注意的是,在做json反序列化时,javaBean可以定义getter方法,但是setter方法必须定义。
再有就是当我们有多个子类的时候,在基类上的注解就会显的很长。我们也有其他的方式可以实现。ObjectMapper类提供了一个registerSubtypes,通过这个方法我们可以直接注册子类,就是说我们不需要在定义基类的时候使用JsonSubTypes这个注解了。

mapper.registerSubtypes(new NamedType(HugeDrink.class, “Huge drink”));
mapper.registerSubtypes(new NamedType(Daedalus.class, “Daedalus”));
mapper.registerSubtypes(new NamedType(StarWand.class, “Star wand”));
上面的这中写法可以达到与JsonSubTypes注解相同的效果。
Demo地址:https://github.com/BigRantLing/JsonSerde

转载:https://zhuanlan.zhihu.com/p/96108902

【转】Jackson之多态反序列化(父类转不同子类)相关推荐

  1. Jackson之多态反序列化

    1.场景描述 JSON作为一种轻量级的数据交换格式,其清晰和简洁的结构能够轻松地与Java对象产生映射关系.例如,一个Coke(可口可乐)类的java代码如下: public class Coke{S ...

  2. 多态之父类引用指向子类对象

    首先,这是多态的知识,多态有三要素: 继承 , 重写父类的方法 , 父类引用指向子类对象 以下是我对于多态的一些理解,如有不当,还请指正! 一,多态的概念与特征: 1.什么多态 多态是同一个行为具有多 ...

  3. java -----多态:父类引用指向子类对象

    生活中的人物视角 程序中的多态 概念:父类引用指向子类对象,从而产生多种形态 一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定 比 ...

  4. Java 理解点整理:父类引用指向子类对象(多态)

    前言 本章主要对"Java的多态"做一个简单的说明. 1.父类引用指向子类对象代码演示 1. 代码:父类Animal,子类Dog,子类Cat//父类:动物 public class ...

  5. Jackson之JSON序列化和多态反序列化

    SerDe是Serialize/Deserilize的简称,即序列化和反序列化. 一.Jackson之序列化和反序列化 JSON作为一种轻量级的数据交换格式,其清晰和简洁的结构能够轻松地与Java对象 ...

  6. Java多态-如何理解父类引用指向子类对象

    java多态,如何理解父类引用指向子类对象 要理解多态性,首先要知道什么是"向上转型". 我定义了一个子类Cat,它继承了Animal类,那么后者就是前者是父类.我可以通过   C ...

  7. Jackson序列化和反序列化

    1,下载Jackson工具包(jackson-core-2.2.3.jar  jackson-annotations-2.2.3.jar  jackson-databind-2.2.3.jar ) j ...

  8. java多态 成员访问特点(父类引用指向子类对象)

    >父类 子类  package com.dareway.demo;public class Father {public String index="fatherLogo"; ...

  9. java父类引用子类_java多态,如何理解父类引用指向子类对象

    要理解多态性,首先要知道什么是"向上转型". 转型是在继承的基础上而言的,继承是面向对象语言中,代码复用的一种机制,通过继承,子类可以复用父类的功能,如果父类不能满足当前子类的需求 ...

最新文章

  1. 国产岚图入局高端智能电动车,对标特斯拉,放话一年内量产
  2. android 全局进度条,Android:如何在中心显示全屏进度条
  3. mysql查看数据类型命令_mysql菜鸟指南(增删查改、数据类型、常用命令)
  4. C#判断字符串是否为数字字符串
  5. 阿帕奇跨域_阿帕奇光束
  6. 给未来的自己一封信计算机,给未来的自己的一封信范文(精选5篇)
  7. Python多进程中多参数问题
  8. Lambda 表达式详解~Streams API~Stream常见接口方法
  9. Git+GitHub+SaltStack
  10. 计算机 映射网络驱动器,映射网络驱动器
  11. drop_caches 释放哪些
  12. Linux Thermal机制源码分析之Thermal zone
  13. 艺龙的执着与固执:等待微信
  14. 1.9无穷小新生五十年
  15. 【理论】基于模型控制和基于数据驱动控制
  16. Quixel bridge无法导入到blender
  17. ocilib 使用_LibOciLib使用说明(2017-1-26更新)
  18. HDU 5804/BC 86A Price List
  19. 抖音小程序配置webview域名
  20. 转战物联网·基础篇02-物联网中的角儿

热门文章

  1. android rotate 动画,Android RotateAnimation详解
  2. pwm调速流程图小车_51单片机 小车 L298N pwm调速 串口控制 按键控制
  3. uniapp微信小程序video全屏苹果xs
  4. div浮动到另一个div上面或者浮动到img图片上面
  5. 门后的秘密-读书笔记
  6. Android代码实现打开打开wifi wps按钮和wps pin码输入
  7. Python - 3.6 学习二
  8. swi 指令能用在C语言吗,SWI指令---软件中断实例详解
  9. 基于C语言的星宿网站框架设计
  10. python init构造函数___Python中的init\uuuu构造函数