SerDe是Serialize/Deserilize的简称,即序列化和反序列化。

一、Jackson之序列化和反序列化

JSON作为一种轻量级的数据交换格式,其清晰和简洁的结构能够轻松地与Java对象产生映射关系。Java开发中常常使用Jackson对JSON文本格式进行序列化和反序列化:

序列化:是指将Java对象转换成Json文件或者Json字符串;

反序列化:是指将Json文件或者Json字符串转换成Java对象。

例如,有如下定义的java的bean:

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

我们可以这样来使用Jackson对一个json文本串进行反序列化和序列化:

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也提供了解决这类问题的方案。

Jackson支持多态类型配置,在进行jackson反序列化时,可以根据配置转换成相应的子类对象。
其配置主要时通过相关的注解@JsonTypeInfo和@JsonSubTypes实现的。

1、@JsonTypeInfo

注解定义结构如下图:

由上图可以看出,这个注解一共有4个字段,分别是use,include,propertydefaultImpl。下面分别对这4个字段进行说明。

(1)  Id类型的use

这个字段时用来指定根据哪种类型的元数据来进行序列化和反序列化(定义使用哪一种类型识别码),可选的值有:

  • JsonTypeInfo.Id.CLASS : 使用完全限定类名做识别
  • sonTypeInfo.Id.MINIMAL_CLASS  :若基类和子类在同一包类,使用类名(忽略包名)作为识别码
  • JsonTypeInfo.Id.NAME : 一个合乎逻辑的指定名称
  • JsonTypeInfo.Id.CUSTOM : 自定义识别码,由@JsonTypeIdResolver对应,稍后解释
  • JsonTypeInfo.Id.NONE : 不使用识别码

这里我们选择的是JsonTypeInfo.Id.NAME这个值,它表示的是我们的Serde将会使用字段名称作为依据。针对上述场景,我们将会根据商品名称来进行serde。

(2) As类型的include
这个字段是用来指定我们的元信息是如何被包含进去的(指定识别码是如何被包含进去的),可选的值如下:

  • JsonTypeInfo.As.PROPERTY : 作为对象的属性
  • JsonTypeInfo.As.EXISTING_PROPERTY : 作为POJO中已经存在的属性
  • JsonTypeInfo.As.EXTERNAL_PROPERTY : 作为扩展属性
  • JsonTypeInfo.As.WRAPPER_OBJECT : 作为一个包装的对象
  • JsonTypeInfo.As.WRAPPER_ARRAY : 作为一个包装的数组

这个字段我们选择的是JsonTypeInfo.As.PROPERTY,它所表示的意思是包含机制将会使用一个具体的属性值。

(3) String类型的property

只用当useJsonTypeInfo.Id.CUSTOM,或者includeJsonTypeInfo.As.PROPERTY时才会配置这个值。这个就可以用来表示具体的依据字段。
下面是该注解的使用 :

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,property = "productName")

这个表示的就是在进行反序列化时,我们依据productName这个字段来区分需要转换成的对象。

(4) Class类型的defaultImpl

可选配置,如果类型识别码不存在或者无效,可以使用该属性来制定反序列化时使用的默认类型。

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

三、示例代码

场景描述:近日,某游戏厂家出品一种新的游戏装备实体卡。玩家购买实体卡通过扫码之后就可以获得相应的道具,这些卡机具收藏价值。而每张卡的道具都是通过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 ExmapleTest {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"));

相关文章:

【1】 https://www.cnblogs.com/lknny/p/5757784.html

【2】https://zhuanlan.zhihu.com/p/96108902

【3】https://www.cnblogs.com/noah-sheng/p/13252475.html

Jackson之JSON序列化和多态反序列化相关推荐

  1. 【Java】用Jackson进行JSON序列化/反序列化操作

    Java类和JSON Speaker类: import java.util.ArrayList; import java.util.Arrays; import java.util.List;publ ...

  2. JacksonUtils Jackson的JSON序列化反序列化

    pom.xml添加依赖 <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactI ...

  3. 使用 Newtonsoft.Json; 序列化正常,反序列化异常的一点思考

    序列化后的字符串都正常,信息完整,但是反序列化却出现了异常. 1. 首先是不能反序列化. 我猜测是我要序列化的类 写的有问题. 我的类实例化的时候,需要向构造函数里传一个数组参数,然后用该数组参数为类 ...

  4. json序列化c语言,C语言JSON序列化/反序列化

    最近想找一个C语言处理嵌套结构体和结构体数组的json库,理想的是能够很容易处理复杂结构体嵌套,并且使用简单的,但是没找到比较合适的,于是打算自己封装一个: 两个问题: C语言结构体本身没有元数据,这 ...

  5. 【转】Jackson之多态反序列化(父类转不同子类)

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

  6. Jackson之多态反序列化

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

  7. JSON序列化和反序列化还有这种玩法

    Mixin对于前端开发者可不陌生,Vue.React等知名前端框架都使用了Mixin.而对于后端开发,尤其是Java后端开发来说Mixin却是一个很陌生的概念.今天来我们通过Jackson让后端开发者 ...

  8. json 反序列化 父子类型_Jaskson精讲第7篇-类继承关系下的JSON序列化与反序列化JsonTypeInfo...

    Jackson是Spring Boot(SpringBoot)默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的, ...

  9. json序列化||反序列化过滤某些属性 之@jsonignore注解

    Jackson相关: 使用Jackson相关的注解时一定要注意自己定义的属性命名是否规范.  命名不规范时会失去效果.(例如Ename ,Eage 为不规范命名."nameE",& ...

最新文章

  1. J0ker的CISSP之路:复习-Information Security Management(4)
  2. docker-compose部署nginx,挂载外置配置文件及项目
  3. 虚拟存储器(虚拟内存Vitual Memory)
  4. 车载电脑中控软件_ 车载手机支架怎么选?教你3招,开车的时候方便快捷
  5. easyui框架前后端交互_Easyui Datagrid增删改及后台交互(java)
  6. java类创建顺序,Java基础----你真的了解java类创建顺序吗?
  7. Hibernate中hql的基本查询、条件查询、排序插叙、分页查询、投影查询
  8. 比较标签 php,比较标签 · ThinkPHP5.0完全开发手册 · 看云
  9. 《浪潮之巅》读书笔记——第6章 Microsoft
  10. Swagger UI 可视化 web API 文档、Multiple Dockets with the same group name are not supported.
  11. Spring AOP之静态代理
  12. vs2010 中文版发布及下载地址
  13. 微众银行风险模型笔试前做的准备(公司介绍+欺诈与反欺诈+sql+机器学习)
  14. 中兴光纤猫 F460 V3.0破解
  15. UCenter Home 正式开源 促进国内SNS网站发展
  16. trips | python注释快捷键
  17. 多屏互动重现双十一“大数据广告诱惑”
  18. 篮球总是提示服务器维护中,范特西篮球经理3月7日服务器维护公告
  19. CVPR2019论文题目中文列表
  20. 三星显示屏测试软件,LCD随意调 三星MagicTune软件全体验

热门文章

  1. 静态IP上网是什么意思
  2. 数据集大全:25个深度学习的开放数据集
  3. 微信视频号的10大引流技巧
  4. 如何把 html、js、css 等一个 html 网页打包成单一的 exe 可执行程序文件?
  5. 2012年2月底—3月初 51Aspx源码发布详情
  6. 嵌入式设计与开发项目-ADC键盘扫描程序设计
  7. fabric-ca服务构建及证书生成
  8. linux debian_Debian Linux | 第1部分
  9. [Android]如何做一个崩溃率少于千分之三噶应用app(17)-组件化SDK
  10. VMware:速修复这三个严重的 Workspace ONE Assist 软件漏洞