什么是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地址:https://github.com/FasterXML/jackson 。

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项。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

web starter中依赖了json starter:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId>
</dependency>

json starter最终引入了Jackson:

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.module</groupId><artifactId>jackson-module-parameter-names</artifactId>
</dependency>

上面已经提到过,jackson-databind依赖于Streaming和Annotations包,因此,引入jackson-databind相当于引入了jackson-core和jackson-annotations。通常情况下,我们单独使用时,根据需要通过Maven引入jackson-databind、jackson-core和jackson-annotations即可。

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId>
</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方法,该属性只能用于反序列化。
@Slf4j
public class JacksonTest {/*** JavaBean转JSON字符串*/@Testpublic void testJavaBeanToJson() {WeChat weChat = new WeChat();weChat.setId("zhuan2quan");weChat.setName("程序新视界");weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});ObjectMapper mapper = new ObjectMapper();try {String result = mapper.writeValueAsString(weChat);System.out.println(result);} catch (JsonProcessingException e) {log.error("转换异常", e);}}/*** JSON字符串转JavaBean*/@Testpublic void testJsonToJavaBean() {String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";ObjectMapper mapper = new ObjectMapper();try {WeChat weChat = mapper.readValue(json, WeChat.class);System.out.println(weChat);} catch (JsonProcessingException e) {log.error("解析异常", e);}}/*** JSON字符串转Map集合*/@Testpublic void testJsonToMap() {String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";ObjectMapper mapper = new ObjectMapper();try {// 对泛型的反序列化,使用TypeReference可以明确的指定反序列化的类型。Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});System.out.println(map);} catch (JsonProcessingException e) {log.error("解析异常", e);}}/*** JavaBean转文件*/@Testpublic void testJavaBeanToFile() {WeChat weChat = new WeChat();weChat.setId("zhuan2quan");weChat.setName("程序新视界");weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});ObjectMapper mapper = new ObjectMapper();try {//写到文件mapper.writeValue(new File("/json.txt"), weChat);//从文件中读取WeChat weChat1 = mapper.readValue(new File("/json.txt"), WeChat.class);System.out.println(weChat1);} catch (IOException e) {log.error("转换异常", e);}}/*** JavaBean转字节流*/@Testpublic void testJavaBeanToBytes() {WeChat weChat = new WeChat();weChat.setId("zhuan2quan");weChat.setName("程序新视界");weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});ObjectMapper mapper = new ObjectMapper();try {// 写为字节流byte[] bytes = mapper.writeValueAsBytes(weChat);// 从字节流读取WeChat weChat1 = mapper.readValue(bytes, WeChat.class);System.out.println(weChat1);} catch (IOException e) {log.error("转换异常", e);}}
}

上述代码用到了lombok注解和单元测试注解,根据需要可进行替换。

JSON树模型

如果Json字符串比较大,则可使用JSON树模型来灵活的获取所需的字段内容。在Jackson中提供了get、path、has等方法来获取或判断。下面直接看两个示例:

@Slf4j
public class JacksonNodeTest {/*** JavaBean转JSON字符串*/@Testpublic void testJsonNode() {// 构建JSON树ObjectMapper mapper = new ObjectMapper();ObjectNode root = mapper.createObjectNode();root.put("id", "zhuan2quan");root.put("name", "程序新视界");ArrayNode interest = root.putArray("interest");interest.add("Java");interest.add("Spring Boot");interest.add("JVM");// JSON树转JSON字符串String json = null;try {json = mapper.writeValueAsString(root);} catch (JsonProcessingException e) {log.error("Json Node转换异常", e);}System.out.println(json);}/*** 解析JSON字符串为JSON树模型*/@Testpublic void testJsonToJsonNode() {String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";ObjectMapper mapper = new ObjectMapper();try {// 将JSON字符串转为JSON树JsonNode jsonNode = mapper.readTree(json);String name = jsonNode.path("name").asText();System.out.println(name);JsonNode interestNode = jsonNode.get("interest");if (interestNode.isArray()){for (JsonNode node : interestNode){System.out.println(node.asText());}}} catch (JsonProcessingException e) {log.error("Json Node转换异常", e);}}
}

其中get方法和path功能相似,区别在于如果要读取的key在Json串中不存在时,get方法会null,而path会返回MissingNode实例对象,在链路方法情况下保证不会抛出异常。

流式API

除了上述两种形式,还可以基于底层的流式API来进行操作,主要通过JsonGenerator和JsonParser两个API,但操作起来比较复杂,就不再这里演示了。

格式化统一配置

在使用ObjectMapper时,会存在一些字段在某些情况下不需要进行序列化或反序列化,同时还可能需要指定格式化的一些信息等。此时,可以通过ObjectMapper进行配置。

//反序列化时忽略json中存在但Java对象不存在的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//序列化时日期格式默认为yyyy-MM-dd'T'HH:mm:ss.SSSZ
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//序列化时自定义时间日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//序列化时忽略值为null的属性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//序列化时忽略值为默认值的属性
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);

针对于配置项,在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作用于字段上,通常用来进行格式化操作。

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date date;

如果JavaBean中的时间字段使用的是JDK8新增的时间日期(LocalDate/LocalTime/LocalDateTime)类型的话,需要添加jackson-datatype-jsr310依赖。在讲依赖部分时,SpringBoot默认引入的依赖中就有这个。当然,还有一些其他的注解,比如@JsonPropertyOrder、@JsonRootName、@JsonAnySetter、@JsonAnyGetter、@JsonNaming等,当使用时参考对应的文档和示例看一下就可以,这里就不再一一列出了。

自定义解析器

如果上面的注解和统一配置还无法满足需求,可自定义解析器,示例如下:

public class MyFastjsonDeserialize extends JsonDeserializer<Point> {@Overridepublic Point deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {JsonNode node = jsonParser.getCodec().readTree(jsonParser);Iterator<JsonNode> iterator = node.get("coordinates").elements();List<Double> list = new ArrayList<>();while (iterator.hasNext()) {list.add(iterator.next().asDouble());}return new Point(list.get(0), list.get(1));}
}

定义完成之后,注册到Mapper中:

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Point.class, new MyFastjsonDeserialize());
objectMapper.registerModule(module);

Jackson处理XML

Jackson也可以通过jackson-dataformat-xml包提供了处理XML的功能。在处理XML时建议使用woodstox-core包,它是一个XML的实现,比JDK自带XML实现更加高效,也更加安全。

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>

如果使用Java 9及以上版本,可能会出现java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException异常,这是因为Java 9实现了JDK的模块化,将原本和JDK打包在一起的JAXB实现分隔出来。所以需要手动添加JAXB的实现。

<dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId>
</dependency>

下面是代码示例,基本上和JSON的API非常相似,XmlMapper实际上就是ObjectMapper的子类。

@Test
public void testXml(){WeChat weChat = new WeChat();weChat.setId("zhuan2quan");weChat.setName("程序新视界");weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});XmlMapper xmlMapper = new XmlMapper();try {String xml = xmlMapper.writeValueAsString(weChat);System.out.println(xml);} catch (JsonProcessingException e) {e.printStackTrace();}
}

执行之后,输出结果:

<WeChat><id>zhuan2quan</id><name>程序新视界</name><interest><interest>Java</interest><interest>Spring Boot</interest><interest>JVM</interest></interest>
</WeChat>

Spring Boot中的集成

在最开始的时候,我们已经看到Spring Boot默认引入了Jackson的依赖,而且也用我们做什么额外的操作,其实已经在使用Jackson进行Json格式的数据与MVC中参数进行绑定操作了。如果Spring Boot默认的配置并不适合项目需求,也可以通过内置的配置进行配置,以application.yml配置为例,可通过指定以下属性进行相应选项的配置:

#指定日期格式,比如yyyy-MM-dd HH:mm:ss,或者具体的格式化类的全限定名
spring.jackson.date-format
#是否开启Jackson的反序列化
spring.jackson.deserialization
#是否开启json的generators.
spring.jackson.generator
#指定Joda date/time的格式,比如yyyy-MM-ddHH:mm:ss). 如果没有配置的话,dateformat会作为backup
spring.jackson.joda-date-time-format
#指定json使用的Locale.
spring.jackson.locale
#是否开启Jackson通用的特性.
spring.jackson.mapper
#是否开启jackson的parser特性.
spring.jackson.parser
#指定PropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)或者指定PropertyNamingStrategy子类的全限定类名.
spring.jackson.property-naming-strategy
#是否开启jackson的序列化.
spring.jackson.serialization
#指定序列化时属性的inclusion方式,具体查看JsonInclude.Include枚举.
spring.jackson.serialization-inclusion
#指定日期格式化时区,比如America/Los_Angeles或者GMT+10.
spring.jackson.time-zone

Spring Boot自动配置非常方便,但某些时候需要我们手动配置Bean来替代自动配置的Bean。可通过如下形式进行配置:

@Configuration
public class JacksonConfig {@Bean@Qualifier("json")public ObjectMapper jsonMapper(Jackson2ObjectMapperBuilder builder) {ObjectMapper mapper = builder.createXmlMapper(false).build();mapper.enable(SerializationFeature.INDENT_OUTPUT);mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);return mapper;}
}

配置完成,可在使用的地方直接注入即可:

@Resource
private ObjectMapper jsonMapper;

对于上面的注入,可能会有朋友问了,是否有线程安全的问题?这个不用担心ObjectMapper是线程安全的。

Json学习总结(9)——放弃FastJson!Jackson的功能原来如此之牛相关推荐

  1. 放弃FastJson!一篇就够,Jackson的功能原来如此之牛(万字干货)

    在上篇<经过多方调研,最终还是决定禁用FastJson!>中,讲了FastJson的基本使用以及存在的不确定性问题,所以最终决定在项目中放弃使用,进而选择市面上比较主流,Spring Bo ...

  2. Json学习总结(8)——经过多方调研,最终还是决定禁用FastJson

    前言 老项目中使用到多种json类库,没有统一管理.最近开启全新的项目,准备对json类库进行统一,这样不仅能够压缩jar包的大小,也能够避免某个类库的漏洞导致系统问题.其实,就在前几个月因为Fast ...

  3. java对象转Json学习

    java对象转Json学习 json多用于数据存储.交换与传输,json相比于xml更小,更快,更容易解析 对象的表示方法 #java Person p = new Person(); p.setNa ...

  4. java ajax解析json数据_利用AJAX向后台servlet传JSON数据,后台利用fastjson进行解析

    一,json的构建 JSON的格式分为两种,第一种类似于JavaScript中的对象(这里需要注意的是JS中的对象与java中的对象概念是不一样的,js可以在对象中定义属性,方法等,类似于java中的 ...

  5. Java 项目 JSON 转 XML(By fastjson)

    Java 项目 JSON 转 XML(By fastjson) package com.test;import java.util.Iterator; import java.util.Map.Ent ...

  6. AJAX+JSON【练习】实现简易的点赞功能

    AJAX+JSON[练习]实现简易的点赞功能 1.前言 2.AJAX实现前后端数据传递 3.JSON与Java对象之间的转换 4.数据库相关配置 5.jQuery的JS文件,lib中的Jar包 6.代 ...

  7. JavaWeb黑马旅游网-学习笔记03【登陆和退出功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  8. JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  9. JavaWeb-综合案例(用户信息)-学习笔记06【复杂条件查询功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...

最新文章

  1. Gym迎来首个完整环境文档,强化学习入门更加简单!
  2. java生成pdf表格_java在pdf中生成表格的方法
  3. RDD(弹性分布式数据集)
  4. mysql手工注入imformation_网络安全系列之十八 MySQL基本操作(PHP注入基础)
  5. Redis数据结构之有序集合
  6. 快手诉“短视频人气助手”软件不正当竞争:索赔100万元
  7. 芬朗DSP电脑调音软件
  8. matlab常用代码程序
  9. 常用VC++运行时下载地址
  10. 如何用计算机还原魔方,初学者使用的魔方口诀 7步教你快速复原三阶魔方(附图解)...
  11. pandas read_csv sep用法
  12. 发现最新的区块链应用-8月16日
  13. mysql 留存率_用mysql统计留存率
  14. Python的turtle模块画爱心箭(附源码)
  15. 关于erp系统最小采购量
  16. python随机生成小写字母表_用小写字母生成大随机字符串的最快方法
  17. 【NLP】BiLSTM 命名实体识别 手写代码
  18. Ktor 2.0?半香不香的尴尬
  19. 加密与安全(个人笔记)
  20. 关于标准ASCII编码表中:数字、小写英文字母和大写英文字母的前后次序

热门文章

  1. 矩阵分析 第三章 内积空间 正规矩阵 Hermite矩阵
  2. mysql4.0升级_再遇MySQL4.0升级到MySQL5.1的时候
  3. php redis auth 高并发,PHP+Redis高并发
  4. oracle表空间管理图形界面,Oracle表空间管理和优化
  5. python制作u盘病毒_十行代码--用Python写一个USB病毒!
  6. pythonlist循环添加元素_list.append()在for循环中每次添加的都是最后的一个元素汗血宝马...
  7. C++之指针探究(六):二级指针和指针数组
  8. mac系统pycharm配置python环境_Python--在Mac下安装PyQt5环境并配合pycharm(全部使用当前最新版)...
  9. 羊车门问题python程序_用Python实现羊车门问题
  10. wk一sm5时间温度控制器_Android RTC 自下而上分析