在上篇《经过多方调研,最终还是决定禁用FastJson!》中,讲了FastJson的基本使用以及存在的不确定性问题,所以最终决定在项目中放弃使用,进而选择市面上比较主流,Spring Boot默认绑定的JSON类库:Jackson。
本文就来讲解一下Jackson的基本使用以及与Spring Boot的结合与实践。
什么是Jackson
Jackson是比较主流的基于Java的JSON类库,可用于Json和XML与JavaBean之间的序列化和反序列化。
没看错,Jackson也可以处理JavaBean与XML之间的转换,基于jackson-dataformat-xml组件,而且比较JDK自带XML实现更加高效和安全。而我们使用比较多的是处理JSON与JavaBean之间的功能。
Jackson主流到什么程度?单从Maven仓库中的统计来看,Jackson的使用量排位第一。而Spring Boot支持的三个JSON库(Gson、Jackson、JSON-B)中,Jackson是首选默认库。
Jackson也有以下特点:依赖少,简单易用,解析大Json速度快、内存占用比较低、拥有灵活的API、方便扩展与定制。
Jackson类库GitHub地址:github.com/FasterXML/j… 。
Jackson的组成部分
Jackson的核心模块由三部分组成(从Jackson 2.x开始):jackson-core、jackson-annotations、jackson-databind。
- jackson-core:核心包,定义了低级流(Streaming)API,提供基于”流模式”解析。Jackson内部实现正是通过高性能的流模式API的JsonGenerator和JsonParser来生成和解析json。
- jackson-annotations,注解(Annotations)包,提供标准的Jackson注解功能;
- jackson-databind:数据绑定(Databind)包,实现了数据绑定(和对象序列化)支持,它依赖于Streaming和Annotations包。提供基于“对象绑定”解析的API(ObjectMapper)和”树模型”解析的API(JsonNode);基于”对象绑定”解析的API和”树模型”解析的API依赖基于“流模式”解析的API。
下面看一下不同环境下相关组件的依赖引入情况。
在SpringBoot当中,spring-boot-starter-web间接引入了Jackson组件,也就是如果你使用了SpringBoot框架,那么你的项目中已经有了Jackson依赖。下面的依赖省略了version和scope项。
1 | xml复制代码<dependency> |
web starter中依赖了json starter:
1 | xml复制代码<dependency> |
json starter最终引入了Jackson:
1 | xml复制代码<dependency> |
上面已经提到过,jackson-databind依赖于Streaming和Annotations包,因此,引入jackson-databind相当于引入了jackson-core和jackson-annotations。
通常情况下,我们单独使用时,根据需要通过Maven引入jackson-databind、jackson-core和jackson-annotations即可。
1 | xml复制代码<dependency> |
对于SpringBoot项目,基本上不用再额外添加依赖。
Jackson核心类ObjectMapper
Jackson提供了三种JSON的处理方式,分别是:数据绑定、JSON树模型、流式API。其中前两项功能都是基于ObjectMapper来实现的,而流式API功能则需要基于更底层的JsonGenerator和JsonParser来实现。
通常情况下我们使用ObjectMapper类就足够了,它拥有以下功能:
- 从字符串、流或文件中解析JSON,并创建表示已解析的JSON的Java对象(反序列化)。
- 将Java对象构建成JSON字符串(序列化)。
- 将JSON解析为自定义类的对象,也可以解析JSON树模型的对象;
ObjectMapper基于JsonParser和JsonGenerator来实现JSON实际的读/写。这一点看一下ObjectMapper的构造方法即可明白。
具体实例
Jackson的常见使用,就不逐一讲解了,通过一些列的实例给大家展示一下,每个实例当中都会通过注释进行说明。
常见简单使用
下面的示例是我们经常会用到的用法演示,主要涉及到JavaBean和Json字符串之间的转换。
Jackson在将json转换为JavaBean属性时,默认是通过Json字段的名称与Java对象中的getter和setter方法进行匹配进行绑定。
Jackson取getter和setter方法名称中去除“get”和“set”部分,并将首字母小写。例如Json中的name,与JavaBean中的getName()和setName()进行匹配。
但并不是所有的属性都可以被序列化和反序列化,基本上遵循一下规则:
- public修饰的属性可序列化和反序列化。
- 属性提供public的getter/setter方法,该属性可序列化和反序列化。
- 属性只有public的setter方法,而无public的getter方法,该属性只能用于反序列化。
1 | swift复制代码@Slf4j |
上述代码用到了lombok注解和单元测试注解,根据需要可进行替换。
JSON树模型
如果Json字符串比较大,则可使用JSON树模型来灵活的获取所需的字段内容。在Jackson中提供了get、path、has等方法来获取或判断。
下面直接看两个示例:
1 | swift复制代码@Slf4j |
其中get方法和path功能相似,区别在于如果要读取的key在Json串中不存在时,get方法会null,而path会返回MissingNode实例对象,在链路方法情况下保证不会抛出异常。
流式API
除了上述两种形式,还可以基于底层的流式API来进行操作,主要通过JsonGenerator和JsonParser两个API,但操作起来比较复杂,就不再这里演示了。
格式化统一配置
在使用ObjectMapper时,会存在一些字段在某些情况下不需要进行序列化或反序列化,同时还可能需要指定格式化的一些信息等。此时,可以通过ObjectMapper进行配置。
1 | scss复制代码//反序列化时忽略json中存在但Java对象不存在的属性 |
针对于配置项,在2.2版本中新增了一个ObjectMapper的实现类JsonMapper,功能与ObjectMapper一致。不过新增了一个builder方法。可以直接通过JsonMapper.builder().configure()方法来进行配置,最后获得一个JsonMapper对象。JsonMapper的其他方法基本都集成自ObjectMapper。
注解的使用
上面通过统一配置可对全局格式的序列化和反序列化进行配置,但某些个别的场景下,需要针对具体的字段进行配置,这就需要用注解。比如当Json字符串中的字段与Java对象中的属性不一致时,就需要通过注解来建立它们直接的关系。
@JsonProperty,作用JavaBean字段上,指定一个字段用于JSON映射,默认情况下映射的JSON字段与注解的字段名称相同。可通过value属性指定映射的JSON的字段名称。
@JsonIgnore可用于字段、getter/setter、构造函数参数上,指定字段不参与序列化和反序列化。
@JsonIgnoreProperties作用于类上,序列化时@JsonIgnoreProperties({“prop1”, “prop2”})会忽略pro1和pro2两个属性。反序列化时@JsonIgnoreProperties(ignoreUnknown=true)会忽略类中不存在的字段。
@JsonFormat作用于字段上,通常用来进行格式化操作。
1 | sql复制代码@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
如果JavaBean中的时间字段使用的是JDK8新增的时间日期(LocalDate/LocalTime/LocalDateTime)类型的话,需要添加jackson-datatype-jsr310依赖。在讲依赖部分时,SpringBoot默认引入的依赖中就有这个。
当然,还有一些其他的注解,比如@JsonPropertyOrder、@JsonRootName、@JsonAnySetter、@JsonAnyGetter、@JsonNaming等,当使用时参考对应的文档和示例看一下就可以,这里就不再一一列出了。
自定义解析器
如果上面的注解和统一配置还无法满足需求,可自定义解析器,示例如下:
1 | scala复制代码public class MyFastjsonDeserialize extends JsonDeserializer<Point> { |
定义完成之后,注册到Mapper中:
1 | ini复制代码ObjectMapper objectMapper = new ObjectMapper(); |
Jackson处理XML
Jackson也可以通过jackson-dataformat-xml包提供了处理XML的功能。在处理XML时建议使用woodstox-core包,它是一个XML的实现,比JDK自带XML实现更加高效,也更加安全。
1 | xml复制代码<dependency> |
如果使用Java 9及以上版本,可能会出现java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException异常,这是因为Java 9实现了JDK的模块化,将原本和JDK打包在一起的JAXB实现分隔出来。所以需要手动添加JAXB的实现。
1 | xml复制代码<dependency> |
下面是代码示例,基本上和JSON的API非常相似,XmlMapper实际上就是ObjectMapper的子类。
1 | java复制代码@Test |
执行之后,输出结果:
1 | xml复制代码<WeChat> |
Spring Boot中的集成
在最开始的时候,我们已经看到Spring Boot默认引入了Jackson的依赖,而且也用我们做什么额外的操作,其实已经在使用Jackson进行Json格式的数据与MVC中参数进行绑定操作了。
如果Spring Boot默认的配置并不适合项目需求,也可以通过内置的配置进行配置,以application.yml配置为例,可通过指定以下属性进行相应选项的配置:
1 | perl复制代码#指定日期格式,比如yyyy-MM-dd HH:mm:ss,或者具体的格式化类的全限定名 |
Spring Boot自动配置非常方便,但某些时候需要我们手动配置Bean来替代自动配置的Bean。可通过如下形式进行配置:
1 | less复制代码@Configuration |
配置完成,可在使用的地方直接注入即可:
1 | java复制代码@Resource |
对于上面的注入,可能会有朋友问了,是否有线程安全的问题?这个不用担心ObjectMapper是线程安全的。
小结
经过本篇文章的讲解,大家对Jackson应该有一个比较全面的了解了。就个人而言,学习Jackson之后,感觉还是挺有意思的。
原文链接:《放弃FastJson!一篇就够,Jackson的功能原来如此之牛(万字干货)》
程序新视界
\
公众号“ 程序新视界”,一个让你软实力、硬技术同步提升的平台,提供海量资料
\
本文转载自: 掘金