使用Jackson库进行JSON序列化和反序列化

如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!​

一、序言

在现代Web开发中,JSON(JavaScript Object Notation)成为了一种广泛使用的数据格式,用于前后端数据传输和存储。Java是一种面向对象编程语言,而JSON是一种键值对格式的数据,因此在Java中,需要将Java对象转换为JSON字符串,或者将JSON字符串转换为Java对象。这个过程就是JSON的序列化和反序列化。

对于Java中的JSON序列化和反序列化,有很多开源库可供选择,其中Jackson库是最受欢迎的之一。Jackson库提供了丰富的功能,可以实现灵活的JSON序列化和反序列化,而且性能非常出色。

本篇文章将介绍Jackson库的JSON序列化和反序列化,包括基本对象、集合、自定义类型、枚举类型、Java时间类型的序列化和反序列化。本文旨在让读者快速了解并使用Jackson库进行JSON序列化和反序列化。

二、什么是JSON序列化和反序列化

JSON序列化是将Java对象转换为JSON字符串的过程。在JSON序列化过程中,Java对象的属性将被转换为JSON对象的键值对,如果Java对象包含其他Java对象或集合,这些嵌套对象也会被转换为嵌套的JSON对象和JSON数组。

JSON反序列化是将JSON字符串转换为Java对象的过程。在JSON反序列化过程中,JSON对象的键值对将被转换为Java对象的属性,如果JSON对象包含其他JSON对象或JSON数组,这些嵌套的JSON也会被转换为嵌套的Java对象和Java集合。

三、Jackson库介绍

Jackson库是一个基于Java的JSON处理库,它提供了一个灵活的JSON解析器和JSON生成器,可以很方便地实现Java对象和JSON数据的转换。Jackson库使用起来非常简单,而且性能非常出色,因此在Java开发中广泛应用。

Jackson库有两个核心类:​​ObjectMapper​​​和​​JsonNode​​。

​ObjectMapper​​​类是Jackson库中最重要的类,它提供了序列化和反序列化Java对象与JSON之间的转换。​​ObjectMapper​​类的实例是线程安全的,可以在多线程环境中共享。

​JsonNode​​​类是一个抽象类,它代表了一个JSON节点。​​JsonNode​​​类有多个子类,例如​​ObjectNode​​​、​​ArrayNode​​​、​​ValueNode​​​等,分别对应JSON中的对象、数组和值。​​JsonNode​​类提供了方便的方法来读取JSON节点的值。

四、基础类型JSON序列化

1、对象序列化

将Java对象转换为JSON字符串最基本的方法就是使用​​ObjectMapper​​​类的​​writeValueAsString​​方法。这个方法接收一个Java对象作为参数,返回一个JSON字符串。

例如:

ObjectMapper mapper = new ObjectMapper();
User user = new User("Tom", 20);
String json = mapper.writeValueAsString(user);

上面的代码中,我们创建了一个​​User​​​对象,并使用​​ObjectMapper​​​类将其序列化为JSON字符串。​​User​​类的定义如下:

public class User {
    private String name;
    private int age;
    
    public User() {
    }
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}

生成的JSON字符串如下:

{"name":"Tom","age":20}

2、集合序列化

除了序列化单个Java对象,Jackson库还支持序列化Java集合,包括​​List​​​、​​Set​​​和​​Map​​​等。可以使用​​ObjectMapper​​​类的​​writeValueAsString​​方法将Java集合序列化为JSON字符串。

ObjectMapper mapper = new ObjectMapper();
List<User> users = new ArrayList<>();
users.add(new User("Tom", 20));
users.add(new User("Jerry", 22));
String json = mapper.writeValueAsString(users);

上面的代码中,我们创建了一个​​List​​​集合,并将两个​​User​​​对象添加到集合中,然后使用​​ObjectMapper​​类将集合序列化为JSON字符串。

生成的JSON字符串如下:

[{"name":"Tom","age":20},{"name":"Jerry","age":22}]

3、序列化枚举类型

在Java中,枚举类型是一种常见的数据类型,它通常用于表示一组有限的值。使用Jackson库将枚举类型序列化为JSON字符串也是常见的操作。下面是一个简单的枚举类型的定义:

public enum Gender {
    MALE, FEMALE
}

要将枚举类型序列化为JSON字符串,我们只需要在类上添加​​@JsonFormat​​注解,并指定序列化的格式。例如,以下代码将会使用大写字母序列化枚举类型:

public class User {
    private String name;
    private int age;
    private Gender gender;
    
    // getters and setters
}

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);

User user = new User();
user.setName("Tom");
user.setAge(20);
user.setGender(Gender.MALE);

String json = mapper.writeValueAsString(user);
System.out.println(json);

输出结果如下:

{
  "name" : "Tom",
  "age" : 20,
  "gender" : "MALE"
}

在上面的代码中,我们先定义了一个​​User​​​类,其中包含一个枚举类型的字段​​gender​​​。然后,我们使用​​ObjectMapper​​​类将​​User​​对象序列化为JSON字符串,并将结果打印出来。可以看到,枚举类型的值已经被序列化为字符串类型了。

4、序列化Java时间类型

在实际应用中,我们经常需要将Java时间类型序列化为JSON字符串,以便传输到其他系统或存储到数据库中。Jackson库提供了非常方便的支持,可以将Java 8时间类型(例如​​java.time.LocalDateTime​​)序列化为JSON字符串。

以下是一个使用Jackson库进行Java时间类型序列化的示例:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class JacksonDemo {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        String json = mapper.writeValueAsString(now);
        System.out.println(json);
        
        LocalDateTime parsed = mapper.readValue(json, LocalDateTime.class);
        System.out.println(parsed.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    }
}

在上面的代码中,我们首先创建了一个​​ObjectMapper​​​对象,并注册了​​JavaTimeModule​​​模块,以便支持Java 8时间类型的序列化。然后,我们使用​​LocalDateTime.now()​​​方法获取当前时间,并将其序列化为JSON字符串。注意,我们使用了​​ZoneId.of("Asia/Shanghai")​​指定了时区为北京时间。

接着,我们使用​​ObjectMapper​​​的​​readValue()​​​方法将JSON字符串反序列化为​​LocalDateTime​​对象。最后,我们将反序列化后的对象格式化为ISO-8601时间格式,并打印输出。

输出结果如下:

"2022-02-23T14:25:23.845"
2022-02-23T14:25:23.845

可以看到,我们成功将当前时间序列化为JSON字符串,并在反序列化时恢复了原始的时间对象。

需要注意的是,在序列化Java时间类型时,Jackson库默认使用ISO-8601时间格式。如果需要使用其他时间格式,可以使用@JsonFormat注解指定时间格式。例如:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime startTime;

在上面的代码中,我们使用​​@JsonFormat​​注解指定了时间格式和时区。这样,Jackson库就会使用指定的时间格式进行时间序列化了。

五、基础类型JSON反序列化

将JSON字符串转换为Java对象的基本方法是使用​​ObjectMapper​​​类的​​readValue​​方法。这个方法接收两个参数:一个JSON字符串和一个Java类,它会将JSON字符串反序列化为指定的Java类对象。

1、反序列化单个对象

首先,我们来看一下如何将JSON字符串反序列化为单个Java对象。假设我们有一个JSON字符串,表示一个​​User​​对象:

{
    "name": "Tom",
    "age": 20
}

可以使用​​ObjectMapper​​​类的​​readValue​​​方法将其反序列化为​​User​​对象:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"Tom\",\"age\":20}";
User user = mapper.readValue(json, User.class);

上面的代码中,我们创建了一个​​ObjectMapper​​​类的实例,并将JSON字符串反序列化为​​User​​对象。

2、反序列化集合对象

除了反序列化单个Java对象,Jackson库还支持反序列化Java集合,包括​​List​​​、​​Set​​​和​​Map​​​等。可以使用​​ObjectMapper​​​类的​​readValue​​方法将JSON字符串反序列化为Java集合。

ObjectMapper mapper = new ObjectMapper();
String json = "[{\"name\":\"Tom\",\"age\":20},{\"name\":\"Jerry\",\"age\":22}]";
List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {});

上面的代码中,我们创建了一个包含两个​​User​​​对象的JSON字符串,并将其反序列化为​​List​​集合。

需要注意的是,由于Java的泛型擦除机制,不能直接将​​List<User>​​传​给​​readValue​​方法,需要使用​​TypeReference​​类来指定集合类型。​​TypeReference​​是Jackson库提供的一个帮助类,用于获取泛型参数的类型信息。

3、反序列化枚举类型

在Java中,枚举类型是一种特殊的数据类型,它定义了一组固定的常量。在JSON数据中,通常使用字符串来表示枚举常量。使用Jackson库可以很方便地将JSON数据中的字符串转换为Java中的枚举常量。

示例代码如下:

首先定义一个枚举类型:

public enum Gender {
    MALE,
    FEMALE
}

然后,假设我们有如下的JSON数据:

{
    "name": "Tom",
    "gender": "MALE"
}

我们可以使用以下代码将JSON数据中的字符串转换为Java中的枚举常量:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\": \"Tom\", \"gender\": \"MALE\"}";
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
Gender gender = Gender.valueOf(map.get("gender").toString());

上述代码首先使用ObjectMapper类创建一个对象,然后将JSON数据解析为一个Map对象。最后,我们可以使用valueOf()方法将Map中的字符串转换为枚举常量。

4、反序列化Java时间类型

在Java中,时间类型是非常常见的数据类型,包括Date、LocalDate、LocalTime、LocalDateTime等。在JSON数据中,时间类型通常使用字符串来表示。使用Jackson库可以很方便地将JSON数据中的字符串转换为Java中的时间类型。

示例代码如下:

假设我们有如下的JSON数据:

{
    "name": "Tom",
    "birthday": "2000-01-01"
}

我们可以使用以下代码将JSON数据中的字符串转换为Java中的LocalDate类型:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\": \"Tom\", \"birthday\": \"2000-01-01\"}";
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
LocalDate birthday = LocalDate.parse(map.get("birthday").toString());

上述代码首先使用ObjectMapper类创建一个对象,然后将JSON数据解析为一个Map对象。最后,我们可以使用parse()方法将Map中的字符串转换为LocalDate类型。其他时间类型的反序列化类似。

六、自定义序列化和反序列化

在某些情况下,Jackson库提供的默认序列化和反序列化行为可能无法满足需求,需要自定义序列化和反序列化规则。例如,在序列化​​User​​对象时,我们希望将年龄按字符串类型序列化,而不是默认的整型类型。

1、自定义序列化

要自定义序列化规则,需要创建一个实现​​JsonSerializer​​​接口的类。​​JsonSerializer​​是Jackson库提供的一个抽象类,用于序列化Java对象为JSON字符串。

下面是一个将​​User​​对象的年龄按字符串类型序列化的示例:

public class AgeToStringSerializer extends JsonSerializer<Integer> {
    @Override
    public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value.toString());
    }
}

在上面的代码中,我们定义了一个名为​​AgeToStringSerializer​​​的类,它继承自​​JsonSerializer​​​类,并覆盖了​​serialize​​​方法。该方法接收三个参数:要序列化的Java对象、用于输出JSON字符串的​​JsonGenerator​​​对象,以及一个​​SerializerProvider​​对象,该对象提供了序列化时需要的一些信息。

在​​serialize​​​方法中,我们将​​value​​​参数转换为字符串类型,并使用​​gen.writeString​​​方法将其写入​​JsonGenerator​​对象中。

接下来,我们需要在​​User​​​类的​​age​​​字段上使用​​@JsonSerialize​​​注解,指定使用自定义的​​AgeToStringSerializer​​类进行序列化:

public class User {
    private String name;
    @JsonSerialize(using = AgeToStringSerializer.class)
    private int age;
    private Address address;
    
    // ...
}

然后,我们就可以像之前一样使用​​ObjectMapper​​​类将​​User​​对象序列化为JSON字符串了:

ObjectMapper mapper = new ObjectMapper();
User user = new User("Tom", 20, new Address("New York", "NY"));
String json = mapper.writeValueAsString(user);

生成的JSON字符串如下:

{"name":"Tom","age":"20","address":{"city":"New York","state":"NY"}}

2、自定义反序列化

与自定义序列化类似,要自定义反序列化规则,需要创建一个实现​​JsonDeserializer​​​接口的类。​​JsonDeserializer​​是Jackson库提供的一个抽象类,用于反序列化JSON字符串为Java对象。

下面是一个将​​User​​对象的年龄从字符串类型反序列化为整型类型的示例:

public class StringToAgeDeserializer extends JsonDeserializer<Integer> {
    @Override
    public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String value = p.getValueAsString();
        return Integer.parseInt(value);
    }
}

在上面的代码中,我们定义了一个名为​​StringToAgeDeserializer​​的类,它继承自​​JsonDeserializer​​类,并覆盖了​​deserialize​​方法。该方法接收两个参数:用于解析JSON字符串的​​JsonParser​​对象和一个​​DeserializationContext​​对象,该对象提供了反序列化时需要的一些信息。

在​​deserialize​​​方法中,我们首先使用​​p.getValueAsString​​​方法获取到JSON字符串中的值,并将其转换为字符串类型。然后,我们使用​​Integer.parseInt​​方法将字符串类型的值转换为整型类型,并返回结果。

接下来,我们需要在​​User​​​类的​​age​​​字段上使用​​@JsonDeserialize​​​注解,指定使用自定义的​​StringToAgeDeserializer​​类进行反序列化:

public class User {
    private String name;
    @JsonSerialize(using = AgeToStringSerializer.class)
    @JsonDeserialize(using = StringToAgeDeserializer.class)
    private int age;
    private Address address;
    
    // ...
}

然后,我们就可以像之前一样使用​​ObjectMapper​​​类将JSON字符串反序列化为​​User​​对象了:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"Tom\",\"age\":\"20\",\"address\":{\"city\":\"New York\",\"state\":\"NY\"}}";
User user = mapper.readValue(json, User.class);

在上面的代码中,我们先定义了一个JSON字符串,并使用​​ObjectMapper​​​类的​​readValue​​​方法将其反序列化为​​User​​​对象。由于JSON字符串中的​​age​​​字段的值是字符串类型,因此在反序列化时会使用我们自定义的​​StringToAgeDeserializer​​​类进行解析。最终,我们将得到一个​​User​​​对象,它的​​age​​字段的值为整型类型的20。

五、常用注解用途和使用方法

1. @JsonProperty

@JsonProperty注解用于指定Java对象中的属性在序列化为JSON数据时的名称。如果没有使用此注解,则默认使用属性的名称。在反序列化时,此注解也用于指定JSON数据中属性的名称对应Java对象中的哪个属性。

示例代码如下:

假设我们有如下的Java对象:

public class Person {
    private String name;
    private int age;

    @JsonProperty("person_gender")
    private Gender gender;

    // getters and setters
}

我们可以使用@JsonProperty注解来指定属性的名称,如上面代码中的gender属性。当将此Java对象序列化为JSON数据时,gender属性会被序列化为"person_gender"字段。

ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.setName("Tom");
person.setAge(20);
person.setGender(Gender.MALE);
String json = mapper.writeValueAsString(person);
System.out.println(json);

输出结果为:

{"name":"Tom","age":20,"person_gender":"MALE"}

2. @JsonFormat

@JsonFormat注解用于指定Java对象中的日期时间类型在序列化为JSON数据时的格式。可以使用该注解指定日期时间格式、时区等。

示例代码如下:

假设我们有如下的Java对象:

public class Person {
    private String name;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private LocalDate birthday;

    // getters and setters
}

我们可以使用@JsonFormat注解来指定日期时间格式,如上面代码中的birthday属性。当将此Java对象序列化为JSON数据时,birthday属性会被序列化为"yyyy-MM-dd"格式的日期字符串。

ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.setName("Tom");
person.setBirthday(LocalDate.of(2000, 1, 1));
String json = mapper.writeValueAsString(person);
System.out.println(json);

输出结果为:

{"name":"Tom","birthday":"2000-01-01"}

3. @JsonIgnore

@JsonIgnore注解用于忽略Java对象中的某个属性在序列化和反序列化时的操作。被该注解标注的属性在序列化为JSON数据时不会被包含,反序列化时也不会被赋值。

示例代码如下:

假设我们有如下的Java对象:

public class Person {
    private String name;
    private int age;

    @JsonIgnore
    private Gender gender;

    // getters and setters
}

我们可以使用@JsonIgnore注解来忽略gender属性,在序列化为JSON数据时不会包含该属性。

ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.setName("Tom");
person.setAge(20);
person.setGender(Gender.MALE);
String json = mapper.writeValueAsString(person);
System.out.println(json);

输出结果为:

{"name":"Tom","age":20}

4. @JsonInclude

@JsonInclude注解用于指定Java对象中某些属性在序列化为JSON数据时的条件,例如,如果该属性为null或默认值,则不包含该属性。

示例代码如下:

假设我们有如下的Java对象:

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
    private String name;
    private Integer age;
    private Gender gender;

    // getters and setters
}

我们可以使用@JsonInclude注解来指定在序列化为JSON数据时,如果age属性为null,则不包含该属性。

ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.setName("Tom");
person.setAge(null);
person.setGender(Gender.MALE);
String json = mapper.writeValueAsString(person);
System.out.println(json);

输出结果为:

{"name":"Tom","gender":"MALE"}

5. @JsonTypeInfo

@JsonTypeInfo注解用于指定Java对象在序列化和反序列化时的类型信息。可以使用该注解来指定子类的实际类型,在反序列化时可以正确地将JSON数据转换为对应的Java对象。

示例代码如下:

假设我们有如下的Java对象:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "dog"),
    @JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public abstract class Animal {
    private String name;

    // getters and setters
}

public class Dog extends Animal {
    private String breed;

    // getters and setters
}

public class Cat extends Animal {
    private String color;

    // getters and setters
}

我们可以使用@JsonTypeInfo注解来指定Animal对象的子类类型信息,在序列化和反序列化时可以正确地将JSON数据转换为对应的Java对象。

ObjectMapper mapper = new ObjectMapper();
Animal dog = new Dog();
dog.setName("Bobby");
((Dog) dog).setBreed("Bulldog");
String json = mapper.writeValueAsString(dog);
System.out.println(json);
Animal animal = mapper.readValue("{\"name\":\"Kitty\",\"type\":\"cat\",\"color\":\"white\"}", Animal.class);
System.out.println(animal.getClass().getName());
System.out.println(((Cat) animal).getColor());

输出结果为:

{"type":"dog","name":"Bobby","breed":"Bulldog"}
com.example.jackson.Cat
white

总结

本文介绍了如何使用Jackson库进行JSON序列化和反序列化。我们首先了解了Jackson库的基本概念和用法,然后详细讲解了如何使用​​ObjectMapper​​类进行序列化和反序列化。在序列化和反序列化过程中,我们还介绍了如何处理日期和集合类型,并讲解了如何自定义序列化和反序列化规则。

使用Jackson库进行JSON序列化和反序列化是Java开发中常用的操作。本文介绍的内容涵盖了Jackson库的大部分用法,相信读者已经对Jackson库有了初步的了解。在实际开发中,可以根据需要选择不同的序列化和反序列化方式,以便更好地满足业务需求。​

如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!

猜你喜欢

转载自blog.csdn.net/bairo007/article/details/132520849