一滴水,用显微镜看,也是一个大世界。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。
✍前言
各位好,我是YourBatman。从本文起,终于要和Jackson的“高级”局部打交道了,也就是数据绑定jackson-databind
模块。通过接触它的高级API,你会继续的发现,后面花那么多篇幅讲的core外围局部是无价之宝的。毕竟村上春树也通知过咱们:人生没有无用的经验嘛。
jackson-databind
蕴含用于Jackson数据处理器的通用 数据绑定性能和树模型。它构建在Streaming API之上,并应用Jackson注解
进行配置。它就是Jackson提供的高层API,是开发者应用得最多的形式,因而重要水平可见一斑。
尽管Jackson最后的用例是JSON数据绑定,但当初它也能够用于其它数据格式,只有存在解析器和生成器实现即可。但须要留神的是:类的命名在很多中央仍旧应用了“JSON”这个词(比方JsonGenerator),只管它与JSON格局没有理论的硬依赖关系。
小贴士:底层流式API应用的I/O进行输入输出,因而实践上是反对任何格局的
版本约定
- Jackson版本:
2.11.0
- Spring Framework版本:
5.2.6.RELEASE
- Spring Boot版本:
2.3.0.RELEASE
从本文开始,新增导包:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId></dependency>
Tips:jackson-databind
模块它强依赖于jackson-core和jackson-annotations,只须要导入此包,另外两个它主动会帮带进来。
这里须要阐明几句:咱们晓得core包中还有个jackson-annotations
,难道不讲了吗?其实不是,是因为独自讲jackson-annotations
并无意义,毕竟注解还得靠数据绑定模块来解析,所以先搞定这个后再杀回去。
✍注释
据我理解,很多小伙伴对Jackson的理解起源于ObjectMapper
,止于ObjectMapper
。那行,作为接触它的第一篇文章咱们就轻松点,以利用为主来整体的意识它。
性能介绍
ObjectMapper是jackson-databind模块最为重要的一个类,它实现了coder对数据绑定的简直所有性能。它是面向用户的高层API,底层依赖于Streaming API来实现读/写。ObjectMapper次要提供的性能点如下:
它提供读取和写入JSON的性能(最重要的性能)
- 一般POJO的序列化/反序列化
- JSON树模型的读/写
它能够被高度定制,以应用不同格调的JSON内容
- 应用Feature进行定制
- 应用可插拔
com.fasterxml.jackson.databind.Module
模块来扩大/丰盛性能
- 它还反对更高级的对象概念:比方多态泛型、对象标识
它还充当了更为高级(更弱小)的API:ObjectReader和ObjectWriter的工厂
ObjectReader
和ObjectWriter
底层亦是依赖于Streaming API实现读写
只管绝大部分的读/写API都通过ObjectMapper裸露进来了,但有些性能函数还是只放在了ObjectReader/ObjectWriter里,比方对于读/写 长序列 的能力你只能通过ObjectReader#readValues(InputStream) / ObjectWriter#writeValues(OutputStream)
去解决,这是设计者无意为之,毕竟这种case很少很少,没必要和罕用的对付在一起嘛。
数据绑定
数据绑定分为简略数据绑定和齐全数据绑定:
- 简略数据绑定:比方绑定int类型、List、Map等…
@Testpublic void test1() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); // 绑定简略类型 和 Map类型 Integer age = objectMapper.readValue("1", int.class); Map map = objectMapper.readValue("{\"name\": \"YourBatman\"}", Map.class); System.out.println(age); System.out.println(map);}
运行程序,输入:
1{name=YourBatman}
- 齐全数据绑定:绑定到任意的Java Bean对象…
筹备一个POJO:
@Data@NoArgsConstructor@AllArgsConstructorpublic class Person { private String name; private Integer age;}
绑定数据到POJO:
@Testpublic void test2() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Person person = objectMapper.readValue("{\"name\": \"YourBatman\", \"age\": 18}", Person.class); System.out.println(person);}
运行程序,输入:
Person(name=YourBatman, age=18)
ObjectMapper的应用
在利用及开发中,ObjectMapper相对是最常应用的,也是你应用Jackson的入口,本文就列列它的那些应用场景。
小贴士:树模型会独自成文介绍,体现出它的重要性
写(序列化)
提供writeValue()
系列办法用于写数据(可写任何类型),也就是咱们常说的序列化。
- writeValue(File resultFile, Object value):写到指标文件里
- writeValue(OutputStream out, Object value):写到输入流
- String writeValueAsString(Object value):写成字符串模式,此办法最为罕用
- writeValueAsBytes(Object value):写成字节数组
byte[]
@Testpublic void test3() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); System.out.println("----------写简略类型----------"); System.out.println(objectMapper.writeValueAsString(18)); System.out.println(objectMapper.writeValueAsString("YourBatman")); System.out.println("----------写汇合类型----------"); System.out.println(objectMapper.writeValueAsString(Arrays.asList(1, 2, 3))); System.out.println(objectMapper.writeValueAsString(new HashMap<String, String>() {{ put("zhName", "A哥"); put("enName", "YourBatman"); }})); System.out.println("----------写POJO----------"); System.out.println(objectMapper.writeValueAsString(new Person("A哥", 18)));}
运行程序,输入:
----------写简略类型----------18"YourBatman"----------写汇合类型----------[1,2,3]{"zhName":"A哥","enName":"YourBatman"}----------写POJO----------{"name":"A哥","age":18}
读(反序列化)
提供readValue()
系列办法用于读数据(个别读字符串类型),也就是咱们常说的反序列化。
readValue(String content, Class<T> valueType)
:读为指定class类型的对象,此办法最罕用readValue(String content, TypeReference<T> valueTypeRef)
:T示意泛型类型,如List<T>
这种类型,个别用于汇合/Map的反序列化- readValue(String content, JavaType valueType):Jackson内置的JavaType类型,后再详解(应用并不多)
@Testpublic void test4() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); System.out.println("----------读简略类型----------"); System.out.println(objectMapper.readValue("18", Integer.class)); // 抛错:JsonParseException 独自的一个串,解析会抛错 // System.out.println(objectMapper.readValue("YourBatman", String.class)); System.out.println("----------读汇合类型----------"); System.out.println(objectMapper.readValue("[1,2,3]", List.class)); System.out.println(objectMapper.readValue("{\"zhName\":\"A哥\",\"enName\":\"YourBatman\"}", Map.class)); System.out.println("----------读POJO----------"); System.out.println(objectMapper.readValue("{\"name\":\"A哥\",\"age\":18}", Person.class));}
运行程序,输入:
----------读简略类型----------18----------读汇合类型----------[1, 2, 3]{zhName=A哥, enName=YourBatman}----------读POJO----------Person(name=A哥, age=18)
不同于序列化,能够把“所有”写成为一个字符串。反序列化场景有它非凡的中央,比方例子中所示:不能反序列化一个“单纯的”字符串。
泛型擦除问题
从例举进去的三个read读办法中,就应该感觉事件还没完,比方这个带泛型的case:
@Testpublic void test5() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); System.out.println("----------读汇合类型----------"); List<Long> list = objectMapper.readValue("[1,2,3]", List.class); Long id = list.get(0); System.out.println(id);}
运行程序,抛错:
----------读汇合类型----------java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long at cn.yourbatman.jackson.core.ObjectMapperDemo.test5(ObjectMapperDemo.java:100) ...
异样栈里指出:Long id = list.get(0);
这一句呈现了类型转换异样,这便是问题起因所在:泛型擦除,参考图示如下(明明泛型类型是Long,但理论装的是Integer类型):
对这种问题,你可能会“动脑筋”思考:写成[1L,2L,3L]
这样行不行。思维很沉闷,奈何事实仍旧残暴,运行抛错:
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('L' (code 76)): was expecting comma to separate Array entries at [Source: (String)"[1L,2L,3L]"; line: 1, column: 4] ...
这是典型的泛型擦除问题。该问题只可能呈现在读(反序列化)上,不能呈现在写上。那么这种问题怎么破?
在解决此问题之前,咱们得先对Java中的泛型擦除有所理解,至多晓得如下两点论断:
- Java 在编译时会在字节码里指令集之外的中央保留局部泛型信息
- 泛型接口、类、办法定义上的所有泛型、成员变量申明处的泛型都会被保留类型信息,其它中央的泛型信息都会被擦除
此问题在开发过程中十分高频,有了此实践作为撑持,A哥提供两种能够解决本问题的计划供以参考:
计划一:利用成员变量保留泛型
理论依据:成员变量的泛型类型不会被擦除
@Testpublic void test6() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); System.out.println("----------读汇合类型----------"); Data data = objectMapper.readValue("{\"ids\" : [1,2,3]}", Data.class); Long id = data.getIds().get(0); System.out.println(id);}@lombok.Dataprivate static class Data { private List<Long> ids;}
运行程序,一切正常:
----------读汇合类型----------1
计划二:应用官网举荐的TypeReference<T>
官网早早就为咱们思考好了这类泛型擦除的问题,所以它提供了TypeReference<T>
不便咱们把泛型类型保留下来,应用起来是十分的不便的:
@Testpublic void test7() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); System.out.println("----------读汇合类型----------"); List<Long> ids = objectMapper.readValue("[1,2,3]", new TypeReference<List<Long>>() { }); Long id = ids.get(0); System.out.println(id);}
运行程序,一切正常:
----------读汇合类型----------1
本计划的理论依据是:泛型接口/类上的泛型类型不会被擦除。
对于泛型擦除状况,解决思路是hold住泛型类型,这样反序列化的时候才不会抓瞎。凡是只有一抓瞎,Jackson就木有方法只能采纳通用/默认类型去装载喽。
加餐
自2.10
版本起,给ObjectMapper提供了一个子类:JsonMapper
,使得语义更加明确,专门用于解决JSON格局。
严格意义上讲,ObjectMapper不局限于解决JSON格局,比方前面会讲到的它的另外一个子类YAMLMapper
用于对Yaml格局的反对(需额定导包,前面见~)
另外,因为构建一个ObjectMapper实例属于高频动作,因而Jackson也顺应潮流的提供了MapperBuilder
构建器(2.10版本起)。咱们能够通过此构建起很容易的失去一个ObjectMapper(以JsonMapper为例)实例来应用:
@Testpublic void test8() throws JsonProcessingException { JsonMapper jsonMapper = JsonMapper.builder() .configure(JsonReadFeature.ALLOW_SINGLE_QUOTES, true) .build(); Person person = jsonMapper.readValue("{'name': 'YourBatman', 'age': 18}", Person.class); System.out.println(person);}
运行程序,失常输入:
Person(name=YourBatman, age=18)
✍总结
本文内容很轻松,讲述了ObjectMapper的日常应用,应用它进行读/写,实现日常性能。
对于写来说比较简单,一个writeValueAsString(obj)
办法走天下;但对于读来说,除了应用readValue(String content, Class<T> valueType)
主动实现数据绑定外,须要特地留神泛型擦除问题:若反序列化成为一个汇合类型(Collection or Map),泛型会被擦除,此时你应该应用readValue(String content, TypeReference<T> valueTypeRef)
办法代替。
小贴士:若你在工程中遇到objectMapper.readValue(xxx, List.class)
这种代码,那必定是有安全隐患的(但不肯定报错)
✔举荐浏览:
- Fastjson到了说再见的时候了
- 1. 初识Jackson -- 世界上最好的JSON库
- 2. 妈呀,Jackson原来是这样写JSON的
- 3. 懂了这些,方敢在简历上说会用Jackson写JSON
- 4. JSON字符串是如何被解析的?JsonParser理解一下
- 5. JsonFactory工厂而已,还蛮有料,这是我没想到的
♥关注A哥♥
Author | A哥(YourBatman) |
---|---|
集体站点 | www.yourbatman.cn |
yourbatman@qq.com | |
微 信 | fsx641385712 |
沉闷平台 | |
公众号 | BAT的乌托邦(ID:BAT-utopia) |
常识星球 | BAT的乌托邦 |
每日文章举荐 | 每日文章举荐 |