定制开发Jackson解析JSON详细教程

目录

JSON 简述

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

JSONJavaScript Object Notation 的缩写,JSON 定制开发是一种基于文本的格式,定制开发可以把它理解为是一个定制开发结构化的数据,定制开发这个结构化数据中可以定制开发包含键值映射、定制开发嵌套对象以及数组等信息

{  "array": [    1,    2,    3  ],  "boolean": true,  "color": "gold",  "null": null,  "number": 123,  "object": {    "a": "b",    "c": "d"  },  "string": "www.wdbyte.com"}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Jackson 介绍

JacksonFastJson 一样,是一个 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

JacksonMaven 依赖

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

  • 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>
  • 1
  • 2
  • 3
  • 4
  • 5

为了方便这篇文章后续的代码演示,我们同时引入 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

ObjectMapper 对象映射器

ObjectMapperJackson 库中最常用的一个类,使用它可以进行 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 类,定义三个属性,名称、年龄以及技能

@Datapublic class Person {    private String name;    private Integer age;    private List<String> skillList;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Java 对象转换成 JSON 字符串

public class PersonTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void pojoToJsonString() throws JsonProcessingException {        Person person = new Person();        person.setName("aLng");        person.setAge(27);        person.setSkillList(Arrays.asList("java", "c++"));        String json = objectMapper.writeValueAsString(person);        System.out.println(json);        String expectedJson = "{\"name\":\"aLng\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";        Assertions.assertEquals(json, expectedJson);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

输出的 JSON 字符串

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

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

mapper.writeValue(new File("result.json"), myResultObject);// 或者byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);// 或者String jsonString = mapper.writeValueAsString(myResultObject);
  • 1
  • 2
  • 3
  • 4
  • 5

Jackson JSON

public class PersonTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void jsonStringToPojo() throws JsonProcessingException {        String expectedJson = "{\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";        Person person = objectMapper.readValue(expectedJson, Person.class);        System.out.println(person);        Assertions.assertEquals(person.getName(), "aLang");        Assertions.assertEquals(person.getSkillList().toString(), "[java, c++]");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出结果

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

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

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

{  "name": "aLang",  "age": 27,  "skillList": [    "java",    "c++"  ]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

下面进行读取转换

ObjectMapper objectMapper = new ObjectMapper();@Testvoid testJsonFilePojo() throws IOException {    File file = new File("src/Person.json");    Person person = objectMapper.readValue(file, Person.class);    // 或者    // person = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);    System.out.println(person);    Assertions.assertEquals(person.getName(), "aLang");    Assertions.assertEquals(person.getSkillList().toString(), "[java, c++]");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

同样输出了 Person 内容

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

JSONList

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

已经存在一个文件 PersonList.json

[  {    "name": "aLang",    "age": 27,    "skillList": [      "java",      "c++"    ]  },  {    "name": "darcy",    "age": 26,    "skillList": [      "go",      "rust"    ]  }]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

ObjectMapper objectMapper = new ObjectMapper();@Testvoid fileToPojoList() throws IOException {    File file = new File("src/EmployeeList.json");    List<Person> personList = objectMapper.readValue(file, new TypeReference<List<Person>>() {});    for (Person person : personList) {        System.out.println(person);    }    Assertions.assertEquals(personList.size(), 2);    Assertions.assertEquals(personList.get(0).getName(), "aLang");    Assertions.assertEquals(personList.get(1).getName(), "darcy");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以输出对象内容

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

JSONMap

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

ObjectMapper objectMapper = new ObjectMapper();@Testvoid jsonStringToMap() throws IOException {    String expectedJson = "{\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";    Map<String, Object> employeeMap = objectMapper.readValue(expectedJson, new TypeReference<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");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以看到 Map 的输出结果

class java.util.LinkedHashMapname:aLangage:27skillList:[java, c++]
  • 1
  • 2
  • 3
  • 4

Jackson 的忽略字段

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

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

ObjectMapper objectMapper = new ObjectMapper();@Testvoid jsonStringToPojoIgnoreProperties() throws IOException {    // UnrecognizedPropertyException    String json = "{\"yyy\":\"xxx\",\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);    Person person = objectMapper.readValue(json, Person.class);      System.out.printf(person.toString());    Assertions.assertEquals(person.getName(), "aLang");    Assertions.assertEquals(person.getSkillList().toString(), "[java, c++]");}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

正常输出

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

Jackson

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

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

@Data@AllArgsConstructor@NoArgsConstructorpublic class Order {      private Integer id;    private Date createTime;    private LocalDateTime updateTime;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Date 类型

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

class OrderTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJson0() throws JsonProcessingException {        Order order = new Order(1, new Date(), null);        String json = objectMapper.writeValueAsString(order);        System.out.println(json);        order = objectMapper.readValue(json, Order.class);        System.out.println(order.toString());        Assertions.assertEquals(order.getId(), 1);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

{"id":1,"createTime":1658320852395,"updateTime":null}Order(id=1, createTime=Wed Jul 20 20:40:52 CST 2022, updateTime=null)
  • 1
  • 2

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

LocalDateTime 类型

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

class OrderTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJson() throws JsonProcessingException {        Order order = new Order(1, new Date(), LocalDateTime.now());        String json = objectMapper.writeValueAsString(order);        System.out.println(json);        order = objectMapper.readValue(json, Order.class);        System.out.println(order.toString());        Assertions.assertEquals(order.getId(), 1);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

运行后会遇到报错

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:             Java 8 date/time type `java.time.LocalDateTime` not supported by default:                 add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"           to enable handling (through reference chain: com.wdbyte.jackson.Order["updateTime"])
  • 1
  • 2
  • 3
  • 4

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

<dependency>    <groupId>com.fasterxml.jackson.datatype</groupId>    <artifactId>jackson-datatype-jsr310</artifactId>    <version>2.13.3</version></dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

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

class OrderTest {    ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();    @Test    void testPojoToJson() throws JsonProcessingException {        Order order = new Order(1, new Date(), LocalDateTime.now());        String json = objectMapper.writeValueAsString(order);        System.out.println(json);        order = objectMapper.readValue(json, Order.class);        System.out.println(order.toString());        Assertions.assertEquals(order.getId(), 1);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

时间格式化

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

@Data@AllArgsConstructor@NoArgsConstructorpublic class Order {    private Integer id;    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")    private Date createTime;    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")    private LocalDateTime updateTime;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

{"id":1,"createTime":"2022-07-20 20:49:46","updateTime":"2022-07-20 20:49:46"}Order(id=1, createTime=Wed Jul 20 20:49:46 CST 2022, updateTime=2022-07-20T20:49:46)
  • 1
  • 2

Jackson 的常用注解

@JsonIgnore

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

@Datapublic class Cat {    private String name;    @JsonIgnore    private Integer age;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

编写单元测试类

class CatTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJson() throws JsonProcessingException {        Cat cat = new Cat();        cat.setName("Tom");        cat.setAge(2);        String json = 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);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果中 age 属性为 null

{"name":"Tom"}
  • 1

@JsonGetter

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

@Datapublic class Cat {    private String name;    private Integer age;    @JsonGetter(value = "catName")    public String getName() {        return name;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

编写单元测试类进行测试

class CatTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJson2() throws JsonProcessingException {        Cat cat = new Cat();        cat.setName("Tom");        cat.setAge(2);        String json = objectMapper.writeValueAsString(cat);        System.out.println(json);        Assertions.assertEquals(json, "{\"age\":2,\"catName\":\"Tom\"}");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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

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

@JsonSetter

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

@Datapublic class Cat {    @JsonSetter(value = "catName")    private String name;    private Integer age;    @JsonGetter(value = "catName")    public String getName() {        return name;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

编写单元测试类进行测试

class CatTest {    ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJson2() throws JsonProcessingException {        String json = "{\"age\":2,\"catName\":\"Tom\"}";        Cat cat = objectMapper.readValue(json, Cat.class);        System.out.println(cat.toString());        Assertions.assertEquals(cat.getName(), "Tom");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出结果

Cat(name=Tom, age=2)
  • 1

@JsonAnySetter

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

@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {    private String name;    private Integer age;    private Map<String, Object> diyMap = new HashMap<>();    @JsonAnySetter    public void otherField(String key, String value) {        this.diyMap.put(key, value);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

编写单元测试用例

class StudentTest {    private ObjectMapper objectMapper = new ObjectMapper();    @Test    void testJsonToPojo() throws JsonProcessingException {        Map<String, Object> map = new HashMap<>();        map.put("name", "aLang");        map.put("age", 18);        map.put("skill", "java");        String json = objectMapper.writeValueAsString(map);        System.out.println(json);        Student student = objectMapper.readValue(json, Student.class);        System.out.println(student);        Assertions.assertEquals(student.getDiyMap().get("skill"), "java");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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

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

@JsonAnyGetter

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

@ToString@AllArgsConstructor@NoArgsConstructorpublic class Student {    @Getter    @Setter    private String name;    @Getter    @Setter    private Integer age;      @JsonAnyGetter    private Map<String, Object> initMap = new HashMap() {{        put("a", 111);        put("b", 222);        put("c", 333);    }};}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

编写单元测试用例

class StudentTest {    private ObjectMapper objectMapper = new ObjectMapper();    @Test    void testPojoToJsonTest() throws JsonProcessingException {        Student student = new Student();        student.setName("aLang");        student.setAge(20);        String json = objectMapper.writeValueAsString(student);        System.out.println(json);             Assertions.assertEquals(json,"{\"name\":\"aLang\",\"age\":20,\"a\":111,\"b\":222,\"c\":333}");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出结果

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

Jackson 总结

  • JacksonJava 中比较流量的 JSON 处理库之一,它是 Spring 的默认 JSON 工具
  • Jackson 主要有三个模块组成,Streaming API 、AnnotationsData Binding
  • Jackson 中的 ObjectMapper 类十分强大,可以进行 JSON 相关处理,同时可以结合注释以及配置进行自定义转换逻辑。
  • Jackson 扩展性很好,如 CSV、XML、YAML 格式处理都对 Jackson 有相应的适配等
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发