2
头图

Like and watch again, the power is unlimited. Search " Program Ape Alang " on WeChat.

This article Github.com/niumoo/JavaNotes and the unread code blog have been included, there are many knowledge points and series of articles.

JSON is no stranger to developers. Most of today's web services, mobile applications, and even the Internet of Things use JSON as a data exchange format. Learning the manipulation tools in JSON format is essential for developers. This article will show you how to use the Jackson open source tool library to perform common operations on JSON.

<!-- more -->

Introduction to JSON

What is JSON ? JSON is the abbreviation of " JavaScript Object Notation ", JSON is a text-based format, it can be understood as a structured data, this structured data can contain key-value mapping, nested objects and arrays and other information.

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

Introducing Jackson

Like FastJson, Jackson is an open source tool library written in Java language that can process JSON. Jackson is widely used, and the Spring framework uses Jackson for JSON processing by default .

Jackson has three core packages, namely Streaming, Databid, and Annotations , through which JSON can be easily manipulated.

  • Streaming is in the jackson-core module. Some stream processing related APIs and specific JSON implementations are defined.
  • Annotations are in the jackson-annotations module, which contains the annotations in Jackson.
  • Databind jackson-databind模块, Streaming包的基础上实现了数据绑定,依赖于---d6a34a14979432b5e7650a07f0f3d352 Streaming Annotations包。

Thanks to Jackson's highly extensible design, many common text formats and tools have corresponding adaptations to Jackson, such as CSV, XML, YAML, and so on.

Jackson Maven dependencies

When using Jackson, in most cases, we only need to add jackson-databind dependency, then we can use the Jackson function, which depends on the following two packages.

image.png

  • 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>

In order to facilitate the subsequent code demonstration of this article, we also introduce Junit for unit testing and Lombok to reduce the coding of 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 object mapper

ObjectMapper is one of the most commonly used classes in the Jackson library, which can be used to quickly convert between Java objects and JSON strings. If you've used FastJson, then the ones in Jackson ObjectMapper are like the JSON classes in FastJson.

There are some commonly used methods in this class:

  • readValue() The method can perform JSON deserialization, such as converting common content into Java objects, such as strings, file streams, byte streams, and byte arrays.
  • writeValue() method can perform JSON serialization operation, and can convert Java objects into JSON strings.

In most cases, ObjectMapper works by mapping the Java Bean object's Get/Set method when it is converted, so it is particularly important to write the Java object's Get/Set method correctly, but ObjectMapper configurations are also provided, for example, the conversion process between Java objects and JSON strings can be customized in the form of configuration or annotations. These are covered in the following sections.

Jackson JSON Basic Operations

As a JSON tool library in Java, processing JSON strings and Java objects is its most basic and most commonly used function. The following examples demonstrate its usage.

Jackson JSON serialization

Write a Person class that defines three properties, name, age, and skills.

 /**
 * @author https://www.wdbyte.com
 */
@Data
public class Person {
    private String name;
    private Integer age;
    private List<String> skillList;
}

Convert Java objects to JSON strings.

 import java.util.Arrays;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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);
    }
}

Output JSON string:

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

Jackson can even directly write the serialized JSON string to a file or read it as a byte array.

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

Jackson JSON deserialization

Post the code directly:

 package com.wdbyte.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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++]");
    }
}

Output result:

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

The above example demonstrates how to use Jackson to deserialize a JSON string into a Java object. In fact, Jackson deserializes the JSON string in the file and the JSON string in the form of bytes.

For example, a JSON content file Person.json is prepared first.

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

Read conversion is performed below.

 ObjectMapper objectMapper = new ObjectMapper();

@Test
void 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++]");
}

The Person content is also output.

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

JSON to List

The above demonstration JSON string is a single object. If JSON is a list of objects, what should I do with Jackson?

A file PersonList.json already exists.

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

Read it and convert it to List<Person> .

 ObjectMapper objectMapper = new ObjectMapper();

@Test
void 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");
}

Object content can be output:

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

JSON to Map

JSON to Map is very useful when we do not have a Java object of an object. The following demonstrates how to use Jackson to convert JSON text into a Map object.

 ObjectMapper objectMapper = new ObjectMapper();

@Test
void 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");
}

You can see the output of the Map:

 class java.util.LinkedHashMap
name:aLang
age:27
skillList:[java, c++]

Jackson ignore fields

If a property that does not exist in a Java class appears in JSON when converting JSON to Java object, you will encounter com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException exception during conversion.

Use objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) to ignore non-existent properties.

 ObjectMapper objectMapper = new ObjectMapper();

@Test
void 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++]");
}

Normal output:

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

Jackson date formatting

Before Java 8 we usually used the java.util.Date class to handle time, but when Java 8 was released a new time class java.time.LocalDateTime was introduced. The two are handled slightly differently in Jackson .

First create an Order class with two time type properties.

 package com.wdbyte.jackson;

import java.time.LocalDateTime;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author https://www.wdbyte.com
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
  
    private Integer id;

    private Date createTime;

    private LocalDateTime updateTime;
}

Date type

Let's create a new test case to test the JSON conversion of two time types.

 package com.wdbyte.jackson;

import java.time.LocalDateTime;
import java.util.Date;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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);
    }

}

In this test code, we only initialize the time of type Date, and the following is the output:

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

You can see that the serialization and deserialization of JSON is performed normally, but the time in JSON is in a timestamp format, which may not be what we want.

LocalDateTime type

Why is there no time of type LocalDateTime ? Because the JSON conversion of the LocalDateTime class will encounter an error by default.

 /**
 * @author https://www.wdbyte.com
 */
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);
    }
}

After running, you will encounter an error:

 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"])

Here we need to add the corresponding data binding support package.

Add dependencies:

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

Then register the dependencies through the findAndRegisterModules() method when defining the ObjectMapper.

 import java.time.LocalDateTime;
import java.util.Date;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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);
    }
}

Running can get normal serialization and deserialization logs, but the serialized time format is still strange.

 {"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)

time formatting

Customize the time format by using annotations @JsonFormat on the fields.

 import java.time.LocalDateTime;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public 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;
}

Run the above example again to get the time formatted JSON string.

 {"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)

Jackson Common Annotations

@JsonIgnore

Use @JsonIgnore to ignore properties in a Java object, which will not participate in JSON serialization and deserialization.

Example:

 package com.wdbyte.jackson;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;

/**
 * @author https://www.wdbyte.com
 */
@Data
public class Cat {

    private String name;

    @JsonIgnore
    private Integer age;
}

Write unit test classes.

 package com.wdbyte.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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);
    }

}

In the output result, the attribute ---adda68f2a9c18284f5131f6f46dcf074 age is null .

 {"name":"Tom"}

@JsonGetter

Use @JsonGetter to customize property names when JSON serializing Java objects.

 import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;

/**
 * @author https://www.wdbyte.com
 */
@Data
public class Cat {

    private String name;

    private Integer age;

    @JsonGetter(value = "catName")
    public String getName() {
        return name;
    }
}

Write unit test classes to test.

 import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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\"}");
    }
}

The output result, name has been set to catName :

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

@JsonSetter

Use @JsonSetter to set the mapping relationship between keys in JSON and Java properties when deserializing JSON.

 package com.wdbyte.jackson;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;
import lombok.Data;

/**
 * @author https://www.wdbyte.com
 * @date 2022/07/17
 */
@Data
public class Cat {
    @JsonSetter(value = "catName")
    private String name;

    private Integer age;

    @JsonGetter(value = "catName")
    public String getName() {
        return name;
    }
}

Write unit test classes to test.

 import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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");
    }
}

Output result:

 Cat(name=Tom, age=2)

@JsonAnySetter

Use @JsonAnySetter to logically process all properties that do not exist in Java objects when deserializing JSON. The following code demonstrates storing the properties that do not exist in a Map collection.

 import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author https://www.wdbyte.com
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public 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);
    }
}

Write unit test cases.

 import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
 * @author https://www.wdbyte.com
 */
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");
    }

}

In the output, you can see that the skill attribute in the JSON is placed in the diyMap collection because it is not in the Java class Student .

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

@JsonAnyGetter

Use @JsonAnyGetter to serialize a Java object with a Map collection in it as the source of properties in JSON. Here's an example.

 import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * @author https://www.wdbyte.com
 */
@ToString
@AllArgsConstructor
@NoArgsConstructor
public 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);
    }};
}

Write unit test cases.

 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}");
    }

}

Output result:

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

Jackson Summary

  • Jackson is one of the more traffic JSON processing libraries in Java, and it is Spring's default JSON tool.
  • Jackson is mainly composed of three modules, Streaming API, Annotations and Data Binding.
  • The ObjectMapper class in Jackson is very powerful, it can perform JSON related processing, and can combine annotations and configuration for custom conversion logic.
  • Jackson's scalability is very good, such as CSV, XML, YAML format processing has corresponding adaptation to Jackson.

As always, article code is hosted at Github.com/niumoo/javaNotes .

<end>

The article is continuously updated, you can search " Program Ape Alang " on WeChat or visit " Program Ape Alang Blog " to read it for the first time. This article has been included in Github.com/niumoo/JavaNotes . There are many knowledge points and series of articles. Welcome to Star.


程序猿阿朗
376 声望1.8k 粉丝

Hello world :)