0


Jackson使用详细介绍

Jackson使用详细介绍


一 . Jackson 介绍

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

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

  • Streaming 在 jackson-core 模块。 定义了一些流处理相关的 API 以及特定的 JSON 实现。
  • Annotations 在 jackson-annotations 模块,包含了 Jackson 中的注解。
  • Databind 在 jackson-databind 模块, 在 Streaming 包的基础上实现了数据绑定,依赖于 Streaming 和 Annotations 包

二. Jackson Maven 依赖

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

在这里插入图片描述

<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 字符串。

四. Jackson JSON 基本操作

1. Jackson JSON 序列化

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

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

/**
 * @author gf
 * @date 2023/1/29
 */@DatapublicclassPerson{privateString name;privateString age;privateList<String> skillList;}

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

/**
 * @author gf
 * @date 2023/1/29
 */publicclassPersonTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidpojoToJsonString()throwsJsonProcessingException{Person person =newPerson();
        person.setName("LaoWang");
        person.setAge(28);
        person.setSkillList(Arrays.asList("java","c++"));String json = objectMapper.writeValueAsString(person);System.out.println(json);String expectedJson ="{\"name\":\"LaoWang\",\"age\":28,\"skillList\":[\"java\",\"c++\"]}";Assertions.assertEquals(json, expectedJson);}}

输出的 JSON 字符串:

{"name":"LaoWang","age":28,"skillList":["java","c++"]}

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

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

2. Jackson JSON 反序列化

直接上代码:

publicclassPersonTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidjsonStringToPojo()throwsJsonProcessingException{String expectedJson ="{\"name\":\"LaoWang\",\"age\":28,\"skillList\":[\"java\",\"c++\"]}";Person person = objectMapper.readValue(expectedJson,Person.class);System.out.println(person);Assertions.assertEquals(person.getName(),"LaoWang");Assertions.assertEquals(person.getSkillList().toString(),"[java, c++]");}}

输出结果:

Person(name=LaoWang, age=28, skillList=[java, c++])

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

3. JSON 转 List

上面演示 JSON 字符串都是单个对象的,如果 JSON 是一个对象列表那么使用 Jackson 该怎么处理呢?
已经存在一个文件 PersonList.json.

[{"name":"LaoWang","age":28,"skillList":["java","c++"]},{"name":"LaoAi","age":31,"skillList":["java","vue","react"]}]

读取它然后转换成 List

publicclassPersonTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidfileToPojoList()throwsIOException{File file =newFile("src/PersonList.json");List<Person> personList = objectMapper.readValue(file,newTypeReference<List<Person>>(){});for(Person person : personList){System.out.println(person);}Assertions.assertEquals(personList.size(),2);Assertions.assertEquals(personList.get(0).getName(),"LaoWang");Assertions.assertEquals(personList.get(1).getName(),"LaoAi");}}

输出对象内容:

Person(name=LaoWang, age=28, skillList=[java, c++])Person(name=LaoAi, age=31, skillList=[java, vue, react])

4. JSON 转 Map

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

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

可以看到 Map 的输出结果:

classjava.util.LinkedHashMap
name:LaoWang
age:28
skillList:[java, c++]

5. Jackson 忽略字段

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

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

publicclassPersonTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidjsonStringToPojoIgnoreProperties()throwsIOException{String json ="{\"yyy\":\"xxx\",\"name\":\"LaoWang\",\"age\":28,\"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(),"LaoWang");Assertions.assertEquals(person.getSkillList().toString(),"[java, c++]");}}

正常输出:

Person(name=LaoWang, age=28, skillList=[java, c++])

6. Jackson 日期格式化

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

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

importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importjava.time.LocalDateTime;importjava.util.Date;/**
 * @author gf
 * @date 2023/1/29
 */@Data@AllArgsConstructor@NoArgsConstructorpublicclassOrder{privateInteger id;privateDate createTime;privateLocalDateTime updateTime;}
Date 类型

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

importjava.util.Date;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.tomato.jackson.entity.Order;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/**
 * @author gf
 * @date 2023/1/29
 */publicclassOrderTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJson0()throwsJsonProcessingException{Order order =newOrder(1,newDate(),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);}}

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

{"id":1,"createTime":1674977689582,"updateTime":null}Order(id=1, createTime=SunJan2915:34:49CST2023, updateTime=null)

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

LocalDateTime 类型

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

/**
 * @author gf
 * @date 2023/1/29
 */publicclassOrderTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJson()throwsJsonProcessingException{Order order =newOrder(1,newDate(),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);}}

运行后会遇到报错:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:Java8 date/time type `java.time.LocalDateTime` not supported by default: add Module"com.fasterxml.jackson.datatype:jackson-datatype-jsr310"toenable handling (through reference chain:com.tomato.jackson.entity.Order["updateTime"])

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

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

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

publicclassOrderTest{ObjectMapper objectMapper =newObjectMapper().findAndRegisterModules();@TestvoidtestPojoToJson()throwsJsonProcessingException{Order order =newOrder(1,newDate(),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);}}

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

{"id":1,"createTime":1674978707833,"updateTime":[2023,1,29,15,51,47,833000000]}Order(id=1, createTime=SunJan2915:51:47CST2023, updateTime=2023-01-29T15:51:47.833)
时间格式化

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

@Data@AllArgsConstructor@NoArgsConstructorpublicclassOrder{privateInteger id;@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="Asia/Shanghai")privateDate createTime;@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="Asia/Shanghai")privateLocalDateTime updateTime;}

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

{"id":1,"createTime":"2023-01-29 15:57:12","updateTime":"2023-01-29 15:57:12"}Order(id=1, createTime=SunJan2915:57:12CST2023, updateTime=2023-01-29T15:57:12)

7. Jackson 常用注解

@JsonIgnore

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

/**
 * @author gf
 * @date 2023/1/29
 */@DatapublicclassCat{privateString name;@JsonIgnoreprivateInteger age;}

编写单元测试类。

publicclassCatTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJson()throwsJsonProcessingException{Cat cat =newCat();
        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);}}

输出结果中 age 属性为 null。

{"name":"Tom"}
@JsonGetter

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

示例:

@DatapublicclassCat{privateString name;privateInteger age;@JsonGetter(value ="catName")publicStringgetName(){return name;}}

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

publicclassCatTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJson2()throwsJsonProcessingException{Cat cat =newCat();
        cat.setName("Tom");
        cat.setAge(2);String json = 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 属性的映射关系。
示例:

@DatapublicclassCat{@JsonSetter(value ="catName")privateString name;privateInteger age;@JsonGetter(value ="catName")publicStringgetName(){return name;}}

编写单元测试类进行测试

publicclassCatTest{ObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJson3()throwsJsonProcessingException{String json ="{\"age\":2,\"catName\":\"Tom\"}";Cat cat = 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 集合中。
示例:

/**
 * @author gf
 * @date 2023/1/29
 */@Data@AllArgsConstructor@NoArgsConstructorpublicclassStudent{privateString name;privateInteger age;privateMap<String,Object> diyMap =newHashMap<>();@JsonAnySetterpublicvoidotherField(String key,String value){this.diyMap.put(key, value);}}

编写单元测试用例。

publicclassStudentTest{privateObjectMapper objectMapper =newObjectMapper();@TestvoidtestJsonToPojo()throwsJsonProcessingException{Map<String,Object> map =newHashMap<>();
        map.put("name","LaoWang");
        map.put("age",28);
        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");}}

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

{"skill":"java","name":"LaoWang","age":28}Student(name=LaoWang, age=28, diyMap={skill=java})
@JsonAnyGetter

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

@ToString@AllArgsConstructor@NoArgsConstructorpublicclassStudent{@Getter@SetterprivateString name;@Getter@SetterprivateInteger age;@JsonAnyGetterprivateMap<String,Object> initMap =newHashMap(){{put("a",111);put("b",222);put("c",333);}};}

编写单元测试用例。

publicclassStudentTest{privateObjectMapper objectMapper =newObjectMapper();@TestvoidtestPojoToJsonTest()throwsJsonProcessingException{Student student =newStudent();
        student.setName("LaoWang");
        student.setAge(28);String json = objectMapper.writeValueAsString(student);System.out.println(json);Assertions.assertEquals(json,"{\"name\":\"LaoWang\",\"age\":28,\"a\":111,\"b\":222,\"c\":333}");}}

输出结果:

{"name":"LaoWang","age":28,"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 有相应的适配等。
标签: junit java 单元测试

本文转载自: https://blog.csdn.net/qq_44936392/article/details/128788927
版权归原作者 浪浪山的猿 所有, 如有侵权,请联系我们删除。

“Jackson使用详细介绍”的评论:

还没有评论