600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Jackson 解析 JSON 详细教程

Jackson 解析 JSON 详细教程

时间:2019-08-23 02:43:37

相关推荐

Jackson 解析 JSON 详细教程

JSON 对于开发者并不陌生,如今的 WEB 服务、移动应用、甚至物联网大多都是以JSON作为数据交换的格式。学习 JSON 格式的操作工具对开发者来说是必不可少的。这篇文章将介绍如何使用Jackson开源工具库对 JSON 进行常见操作。

JSON logo

JSON 介绍

什么是JSON?JSON 是 ”JavaScript Object Notation“ 的缩写,JSON 是一种基于文本的格式,可以把它理解为是一个结构化的数据,这个结构化数据中可以包含键值映射、嵌套对象以及数组等信息。

{"array":[1,2,3],"boolean":true,"color":"gold","null":null,"number":123,"object":{"a":"b","c":"d"},"string":""}

Jackson 介绍

Jackson和 FastJson 一样,是一个 Java 语言编写的,可以进行 JSON 处理的开源工具库,Jackson 的使用非常广泛,Spring 框架默认使用 Jackson 进行 JSON 处理。

Jackson 有三个核包,分别是Streaming、Databid、Annotations,通过这些包可以方便的对 JSON 进行操作。

•Streaming[1]在jackson-core模块。定义了一些流处理相关的 API 以及特定的 JSON 实现。

•Annotations[2]在jackson-annotations模块,包含了 Jackson 中的注解。

•Databind[3]在jackson-databind模块, 在Streaming包的基础上实现了数据绑定,依赖于StreamingAnnotations包。

得益于 Jackson 高扩展性的设计,有很多常见的文本格式以及工具都有对 Jackson 的相应适配,如 CSV、XML、YAML 等。

Jackson Maven 依赖

在使用 Jackson 时,大多数情况下我们只需要添加jackson-databind依赖项,就可以使用 Jackson 功能了,它依赖了下面两个包。

jackson-databind 依赖

•com.fasterxml.jackson.core:jackson-annotations

•com.fasterxml.jackson.core:jackson-core

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.3</version></dependency>

为了方便这篇文章后续的代码演示,我们同时引入 Junit 进行单元测试和 Lombok 以减少 Get/Set 的代码编写。

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency>

ObjectMapper 对象映射器

ObjectMapper是 Jackson 库中最常用的一个类,使用它可以进行 Java 对象和 JSON 字符串之间快速转换。如果你用过 FastJson,那么 Jackson 中的ObjectMapper就如同 FastJson 中的 JSON 类。

这个类中有一些常用的方法:

readValue()方法可以进行 JSON 的反序列化操作,比如可以将字符串、文件流、字节流、字节数组等将常见的内容转换成 Java 对象。

writeValue()方法可以进行 JSON 的序列化操作,可以将 Java 对象转换成 JSON 字符串。

大多数情况下,ObjectMapper的工作原理是通过 Java Bean 对象的 Get/Set 方法进行转换时映射的,所以正确编写 Java 对象的 Get/Set 方法尤为重要,不过ObjectMapper也提供了诸多配置,比如可以通过配置或者注解的形式对 Java 对象和 JSON 字符串之间的转换过程进行自定义。这些在下面部分都会介绍到。

Jackson JSON 基本操作

Jackson 作为一个 Java 中的 JSON 工具库,处理 JSON 字符串和 Java 对象是它最基本最常用的功能,下面通过一些例子来演示其中的用法。

Jackson JSON 序列化

编写一个 Person 类,定义三个属性,名称、年龄以及技能。

/***@author*/@DatapublicclassPerson{privateStringname;privateIntegerage;privateList<String>skillList;}

将 Java 对象转换成 JSON 字符串。

importjava.util.Arrays;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classPersonTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidpojoToJsonString()throwsJsonProcessingException{Personperson=newPerson();person.setName("aLng");person.setAge(27);person.setSkillList(Arrays.asList("java","c++"));Stringjson=objectMapper.writeValueAsString(person);System.out.println(json);StringexpectedJson="{\"name\":\"aLng\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";Assertions.assertEquals(json,expectedJson);}}

输出的 JSON 字符串:

{"name":"aLng","age":27,"skillList":["java","c++"]}

Jackson 甚至可以直接把序列化后的 JSON 字符串写入文件或者读取成字节数组。

mapper.writeValue(newFile("result.json"),myResultObject);//或者byte[]jsonBytes=mapper.writeValueAsBytes(myResultObject);//或者StringjsonString=mapper.writeValueAsString(myResultObject);

Jackson JSON 反序列化

直接贴出代码:

packagecom.wdbyte.jackson;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classPersonTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidjsonStringToPojo()throwsJsonProcessingException{StringexpectedJson="{\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";Personperson=objectMapper.readValue(expectedJson,Person.class);System.out.println(person);Assertions.assertEquals(person.getName(),"aLang");Assertions.assertEquals(person.getSkillList().toString(),"[java,c++]");}}

输出结果:

Person(name=aLang,age=27,skillList=[java,c++])

上面的例子演示了如何使用 Jackson 把一个 JSON 字符串反序列化成 Java 对象,其实 Jackson 对文件中的 JSON 字符串、字节形式的 JSON 字符串反序列化同样简单。

比如先准备了一个 JSON 内容文件 Person.json。

{"name":"aLang","age":27,"skillList":["java","c++"]}

下面进行读取转换。

ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestJsonFilePojo()throwsIOException{Filefile=newFile("src/Person.json");Personperson=objectMapper.readValue(file,Person.class);//或者//person=mapper.readValue(newURL("/api/entry.json"),MyValue.class);System.out.println(person);Assertions.assertEquals(person.getName(),"aLang");Assertions.assertEquals(person.getSkillList().toString(),"[java,c++]");}

同样输出了 Person 内容。

Person(name=aLang,age=27,skillList=[java,c++])

JSON 转 List

上面演示 JSON 字符串都是单个对象的,如果 JSON 是一个对象列表那么使用 Jackson 该怎么处理呢?

已经存在一个文件PersonList.json.

[{"name":"aLang","age":27,"skillList":["java","c++"]},{"name":"darcy","age":26,"skillList":["go","rust"]}]

读取它然后转换成List<Person>

ObjectMapperobjectMapper=newObjectMapper();@TestvoidfileToPojoList()throwsIOException{Filefile=newFile("src/EmployeeList.json");List<Person>personList=objectMapper.readValue(file,newTypeReference<List<Person>>(){});for(Personperson:personList){System.out.println(person);}Assertions.assertEquals(personList.size(),2);Assertions.assertEquals(personList.get(0).getName(),"aLang");Assertions.assertEquals(personList.get(1).getName(),"darcy");}

可以输出对象内容:

Person(name=aLang,age=27,skillList=[java,c++])Person(name=darcy,age=26,skillList=[go,rust])

JSON 转 Map

JSON 转 Map 在我们没有一个对象的 Java 对象时十分实用,下面演示如何使用 Jackson 把 JSON 文本转成 Map 对象。

ObjectMapperobjectMapper=newObjectMapper();@TestvoidjsonStringToMap()throwsIOException{StringexpectedJson="{\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";Map<String,Object>employeeMap=objectMapper.readValue(expectedJson,newTypeReference<Map>(){});System.out.println(employeeMap.getClass());for(Entry<String,Object>entry:employeeMap.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}Assertions.assertEquals(employeeMap.get("name"),"aLang");}

可以看到 Map 的输出结果:

classjava.util.LinkedHashMapname:aLangage:27skillList:[java,c++]

Jackson 忽略字段

如果在进行 JSON 转 Java 对象时,JSON 中出现了 Java 类中不存在的属性,那么在转换时会遇到com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException异常。

使用objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)可以忽略不存在的属性。

ObjectMapperobjectMapper=newObjectMapper();@TestvoidjsonStringToPojoIgnoreProperties()throwsIOException{//UnrecognizedPropertyExceptionStringjson="{\"yyy\":\"xxx\",\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);Personperson=objectMapper.readValue(json,Person.class);System.out.printf(person.toString());Assertions.assertEquals(person.getName(),"aLang");Assertions.assertEquals(person.getSkillList().toString(),"[java,c++]");}

正常输出:

Person(name=aLang,age=27,skillList=[java,c++])

Jackson 日期格式化

在 Java 8 之前我们通常使用java.util.Date类来处理时间,但是在 Java 8 发布时引入了新的时间类java.time.LocalDateTime. 这两者在 Jackson 中的处理略有不同。

先创建一个有两种时间类型属性的 Order 类。

packagecom.wdbyte.jackson;importjava.time.LocalDateTime;importjava.util.Date;importcom.fasterxml.jackson.annotation.JsonFormat;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;/***@author*/@Data@AllArgsConstructor@NoArgsConstructorpublicclassOrder{privateIntegerid;privateDatecreateTime;privateLocalDateTimeupdateTime;}

Date 类型

下面我们新建一个测试用例来测试两种时间类型的 JSON 转换。

packagecom.wdbyte.jackson;importjava.time.LocalDateTime;importjava.util.Date;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classOrderTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJson0()throwsJsonProcessingException{Orderorder=newOrder(1,newDate(),null);Stringjson=objectMapper.writeValueAsString(order);System.out.println(json);order=objectMapper.readValue(json,Order.class);System.out.println(order.toString());Assertions.assertEquals(order.getId(),1);}}

在这个测试代码中,我们只初始化了 Date 类型的时间,下面是输出的结果:

{"id":1,"createTime":1658320852395,"updateTime":null}Order(id=1,createTime=WedJul:40:52CST,updateTime=null)

可以看到正常的进行了 JSON 的序列化与反序列化,但是 JSON 中的时间是一个时间戳格式,可能不是我们想要的。

LocalDateTime 类型

为什么没有设置LocalDateTime类型的时间呢?因为默认情况下进行 LocalDateTime 类的 JSON 转换会遇到报错。

/***@author*/classOrderTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJson()throwsJsonProcessingException{Orderorder=newOrder(1,newDate(),LocalDateTime.now());Stringjson=objectMapper.writeValueAsString(order);System.out.println(json);order=objectMapper.readValue(json,Order.class);System.out.println(order.toString());Assertions.assertEquals(order.getId(),1);}}

运行后会遇到报错:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:Java8date/timetype`java.time.LocalDateTime`notsupportedbydefault:addModule"com.fasterxml.jackson.datatype:jackson-datatype-jsr310"toenablehandling(throughreferencechain:com.wdbyte.jackson.Order["updateTime"])

这里我们需要添加相应的数据绑定支持包。

添加依赖:

<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.3</version></dependency>

然后在定义 ObjectMapper 时通过findAndRegisterModules()方法来注册依赖。

importjava.time.LocalDateTime;importjava.util.Date;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classOrderTest{ObjectMapperobjectMapper=newObjectMapper().findAndRegisterModules();@TestvoidtestPojoToJson()throwsJsonProcessingException{Orderorder=newOrder(1,newDate(),LocalDateTime.now());Stringjson=objectMapper.writeValueAsString(order);System.out.println(json);order=objectMapper.readValue(json,Order.class);System.out.println(order.toString());Assertions.assertEquals(order.getId(),1);}}

运行可以得到正常序列化与反序列化日志,不过序列化后的时间格式依旧奇怪。

{"id":1,"createTime":1658321191562,"updateTime":[,7,20,20,46,31,567000000]}Order(id=1,createTime=WedJul:46:31CST,updateTime=-07-20T20:46:31.567)

时间格式化

通过在字段上使用注解@JsonFormat来自定义时间格式。

importjava.time.LocalDateTime;importjava.util.Date;importcom.fasterxml.jackson.annotation.JsonFormat;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublicclassOrder{privateIntegerid;@JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="Asia/Shanghai")privateDatecreateTime;@JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="Asia/Shanghai")privateLocalDateTimeupdateTime;}

再次运行上面的列子可以得到时间格式化后的 JSON 字符串。

{"id":1,"createTime":"-07-:49:46","updateTime":"-07-:49:46"}Order(id=1,createTime=WedJul:49:46CST,updateTime=-07-20T20:49:46)

Jackson 常用注解

@JsonIgnore

使用@JsonIgnore可以忽略某个 Java 对象中的属性,它将不参与 JSON 的序列化与反序列化。

示例:

packagecom.wdbyte.jackson;importcom.fasterxml.jackson.annotation.JsonIgnore;importlombok.Data;/***@author*/@DatapublicclassCat{privateStringname;@JsonIgnoreprivateIntegerage;}

编写单元测试类。

packagecom.wdbyte.jackson;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classCatTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJson()throwsJsonProcessingException{Catcat=newCat();cat.setName("Tom");cat.setAge(2);Stringjson=objectMapper.writeValueAsString(cat);System.out.println(json);Assertions.assertEquals(json,"{\"name\":\"Tom\"}");cat=objectMapper.readValue(json,Cat.class);Assertions.assertEquals(cat.getName(),"Tom");Assertions.assertEquals(cat.getAge(),null);}}

输出结果中age属性为null

{"name":"Tom"}

@JsonGetter

使用@JsonGetter可以在对 Java 对象进行 JSON 序列化时自定义属性名称。

importcom.fasterxml.jackson.annotation.JsonGetter;importcom.fasterxml.jackson.annotation.JsonIgnore;importlombok.Data;/***@author*/@DatapublicclassCat{privateStringname;privateIntegerage;@JsonGetter(value="catName")publicStringgetName(){returnname;}}

编写单元测试类进行测试。

importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classCatTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJson2()throwsJsonProcessingException{Catcat=newCat();cat.setName("Tom");cat.setAge(2);Stringjson=objectMapper.writeValueAsString(cat);System.out.println(json);Assertions.assertEquals(json,"{\"age\":2,\"catName\":\"Tom\"}");}}

输出结果,name已经设置成了catName

{"age":2,"catName":"Tom"}

@JsonSetter

使用@JsonSetter可以在对 JSON 进行反序列化时设置 JSON 中的 key 与 Java 属性的映射关系。

packagecom.wdbyte.jackson;importcom.fasterxml.jackson.annotation.JsonGetter;importcom.fasterxml.jackson.annotation.JsonIgnore;importcom.fasterxml.jackson.annotation.JsonSetter;importlombok.Data;/***@author*@date/07/17*/@DatapublicclassCat{@JsonSetter(value="catName")privateStringname;privateIntegerage;@JsonGetter(value="catName")publicStringgetName(){returnname;}}

编写单元测试类进行测试。

importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classCatTest{ObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJson2()throwsJsonProcessingException{Stringjson="{\"age\":2,\"catName\":\"Tom\"}";Catcat=objectMapper.readValue(json,Cat.class);System.out.println(cat.toString());Assertions.assertEquals(cat.getName(),"Tom");}}

输出结果:

Cat(name=Tom,age=2)

@JsonAnySetter

使用@JsonAnySetter可以在对 JSON 进行反序列化时,对所有在 Java 对象中不存在的属性进行逻辑处理,下面的代码演示把不存在的属性存放到一个 Map 集合中。

importjava.util.Map;importcom.fasterxml.jackson.annotation.JsonAnyGetter;importcom.fasterxml.jackson.annotation.JsonAnySetter;mon.collect.Maps;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;/***@author*/@Data@AllArgsConstructor@NoArgsConstructorpublicclassStudent{privateStringname;privateIntegerage;privateMap<String,Object>diyMap=newHashMap<>();@JsonAnySetterpublicvoidotherField(Stringkey,Stringvalue){this.diyMap.put(key,value);}}

编写单元测试用例。

importjava.util.HashMap;importjava.util.Map;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/***@author*/classStudentTest{privateObjectMapperobjectMapper=newObjectMapper();@TestvoidtestJsonToPojo()throwsJsonProcessingException{Map<String,Object>map=newHashMap<>();map.put("name","aLang");map.put("age",18);map.put("skill","java");Stringjson=objectMapper.writeValueAsString(map);System.out.println(json);Studentstudent=objectMapper.readValue(json,Student.class);System.out.println(student);Assertions.assertEquals(student.getDiyMap().get("skill"),"java");}}

输出结果中可以看到 JSON 中的skill属性因为不在 Java 类Student中,所以被放到了diyMap集合。

{"skill":"java","name":"aLang","age":18}Student(name=aLang,age=18,diyMap={skill=java})

@JsonAnyGetter

使用@JsonAnyGetter可以在对 Java 对象进行序列化时,使其中的 Map 集合作为 JSON 中属性的来源。下面做个示例。

importjava.util.HashMap;importjava.util.Map;importcom.fasterxml.jackson.annotation.JsonAnyGetter;importcom.fasterxml.jackson.annotation.JsonAnySetter;mon.collect.Maps;importlombok.AllArgsConstructor;importlombok.Data;importlombok.Getter;importlombok.NoArgsConstructor;importlombok.Setter;importlombok.ToString;/***@author*/@ToString@AllArgsConstructor@NoArgsConstructorpublicclassStudent{@Getter@SetterprivateStringname;@Getter@SetterprivateIntegerage;@JsonAnyGetterprivateMap<String,Object>initMap=newHashMap(){{put("a",111);put("b",222);put("c",333);}};}

编写单元测试用例。

classStudentTest{privateObjectMapperobjectMapper=newObjectMapper();@TestvoidtestPojoToJsonTest()throwsJsonProcessingException{Studentstudent=newStudent();student.setName("aLang");student.setAge(20);Stringjson=objectMapper.writeValueAsString(student);System.out.println(json);Assertions.assertEquals(json,"{\"name\":\"aLang\",\"age\":20,\"a\":111,\"b\":222,\"c\":333}");}}

输出结果:

{"name":"aLang","age":20,"a":111,"b":222,"c":333}

Jackson 总结

• Jackson 是 Java 中比较流量的 JSON 处理库之一,它是 Spring 的默认 JSON 工具。

• Jackson 主要有三个模块组成,Streaming API 、Annotations 和 Data Binding 。

• Jackson 中的 ObjectMapper 类十分强大,可以进行 JSON 相关处理,同时可以结合注释以及配置进行自定义转换逻辑。

• Jackson 扩展性很好,如 CSV、XML、YAML 格式处理都对 Jackson 有相应的适配等。

一如既往,文章代码都存放在/niumoo/javaNotes[4].

引用链接

[1]Streaming:/FasterXML/jackson-core

[2]Annotations:/FasterXML/jackson-annotations

[3]Databind:/FasterXML/jackson-databind

[4]/niumoo/javaNotes:/niumoo/JavaNotes/tree/master/tool-java-jackson

推荐:最全的java面试题库PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。