Skip to content

Jackson 1-使用ObjectMapper进行json序列化与反序列化

本文主要介绍如何使用Jackson库中的ObjectMapper进行json序列化与反序列化。

参考文章链接:https://www.baeldung.com/jackson-object-mapper-tutorial。

1. 引入Jackson

首先使用maven在项目中引入Jackson:

xml
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>

该依赖会自动引入以下两个库:

  • jackson-annotations
  • jackson-core

注意,由于Spring框架自动引入了Jackson,所以我们不必自动引入。

2. 使用ObjectMapper进行json序列化

2.1 简单对象

首先准备一个简单类User

java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String address;
    private boolean adminFlag;
}

然后使用ObjectMapperwriteValueAsString方法,将对象转换为json字符串:

java
@Test
void test01() throws JsonProcessingException {
    User user = new User(1,"张三","广东",false);

    ObjectMapper objectMapper = new ObjectMapper();
    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果如下:

java
{"id":1,"name":"张三","address":"广东","adminFlag":false}

2.2 Date时间格式化

如果对象中带有Date属性,那么我们可以通过设置ObjectMapper的时间格式进行格式化输出:

java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String address;
    private boolean adminFlag;

    private Date birthday;
}
java
@Test
void test01() throws JsonProcessingException {
    User user = new User(1,"张三","广东",false, new Date());

    ObjectMapper objectMapper = new ObjectMapper();
    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果如下:

txt
{"id":1,"name":"张三","address":"广东","adminFlag":false,"birthday":1734163323396}

可以看到Date字段被输出为时间戳,可读性非常差。我们可以设置时间格式来改变这一点:

java
@Test
void test01() throws JsonProcessingException {
    User user = new User(1,"张三","广东",false, new Date());

    ObjectMapper objectMapper = new ObjectMapper();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    objectMapper.setDateFormat(df);

    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果如下:

txt
{"id":1,"name":"张三","address":"广东","adminFlag":false,"birthday":"2024-12-14 16:04:48"}

2.3 Java 8日期时间格式化

User类中新增一个属性:

java
private LocalDateTime createDateTime;

然后直接使用ObjectMapper序列化时报错:

错误信息

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

报错信息提示我们需要引入如下依赖:

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

然后调用如下方法:

java
@Test
void test01() throws JsonProcessingException {
    User user = new User(1,"张三","广东",false,new Date(), LocalDateTime.now());

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.findAndRegisterModules();

    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

之后就可以正常进行序列化了。结果如下:

java
{"id":1,"name":"张三","address":"广东","adminFlag":false,"birthday":1734166284616,"createDateTime":[2024,12,14,16,51,24,622111000]}

可以看到上述方式可读性较差,我们也可以使用如下方式格式化Java 8日期时间输出:

java
@Test
void test01() throws JsonProcessingException {
    User user = new User(1,"张三","广东",false,new Date(), LocalDateTime.now());

    ObjectMapper objectMapper = new ObjectMapper();
//  objectMapper.findAndRegisterModules();

    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    objectMapper.registerModule(javaTimeModule);

    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果如下:

txt
{"id":1,"name":"张三","address":"广东","adminFlag":false,"birthday":1734166422342,"createDateTime":"2024-12-14 16:53:42"}

3 使用ObjectMappe进行json反序列化

3.1 简单对象

我们以上面序列化的字符串作为数据源,进行反序列化:

java
@Test
void test02() throws JsonProcessingException {
    String jsonString = "{\"id\":1,\"name\":\"张三\",\"address\":\"广东\",\"adminFlag\":false,\"birthday\":1734166422342,\"createDateTime\":\"2024-12-14 16:53:42\"}";

    ObjectMapper objectMapper = new ObjectMapper();

    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    objectMapper.registerModule(javaTimeModule);

    User user = objectMapper.readValue(jsonString, User.class);
    System.out.println(user);
}

结果如下:

txt
User(id=1, name=张三, address=广东, adminFlag=false, birthday=Sat Dec 14 16:53:42 CST 2024, createDateTime=2024-12-14T16:53:42)

注意点:

  • 第8行添加的是反序列化处理器LocalDateTimeDeserializer
  • 第11行使用readValue方法进行反序列化,给定json字符串要目标对象类型即可;

3.2 List对象

java
@Test
void test03() throws JsonProcessingException {
    String jsonString ="[{\"id\":1,\"name\":\"张三\",\"address\":\"广东\",\"adminFlag\":false,\"birthday\":1,\"createDateTime\":\"2024-12-13 11:00:00\"},{\"id\":2,\"name\":\"李四\",\"address\":\"上海\",\"adminFlag\":true,\"birthday\":2,\"createDateTime\":\"2024-12-13 12:00:00\"}]";

    ObjectMapper objectMapper = new ObjectMapper();

    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    objectMapper.registerModule(javaTimeModule);

    List<User> userList = objectMapper.readValue(jsonString, new TypeReference<List<User>>(){});
    System.out.println(userList);
}

注意第十一行,指定目标对象类型时,使用new TypeReference<List<User>>(){}方式,同理,MapSet对象也类似处理。

4. 配置ObjectMapper对象

在序列化或反序列化过程中,我们可以对ObjectMapper对象进行配置,以达到某些特殊效果。

我们以简化的User为例:

java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String address;
    private boolean adminFlag;
}

4.1FAIL_ON_UNKNOWN_PROPERTIES

java
@Test
void test04() throws JsonProcessingException {
    String jsonString = "{\"id\":null,\"name\":\"张三\",\"address\":\"广东\",\"adminFlag\":false,\"birthday\":1}";

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    User user = objectMapper.readValue(jsonString, User.class);
    System.out.println(user);
}

DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES配置用于反序列化,如果json字符串中包含多余的属性,那么默认情况下会失败。将该配置设为false

,则不会失败。

注意

在上面的例子中,我们将id设置为null,而id是原始类型,但是并不会报错,结果id取的是默认值0。

这是因为配置DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES默认为false。如果我们将该配置设为true,那么就会报错。

4.2 序列化时忽略null属性

java
@Test
void test05() throws JsonProcessingException {
    User user = new User(1, "章三", null, false);

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

4.3 序列化时保持缩进

java
@Test
void test05() throws JsonProcessingException {
    User user = new User(1, "章三", null, false);

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果如下:

txt
{
  "id" : 1,
  "name" : "章三",
  "address" : null,
  "adminFlag" : false
}

4.4 处理枚举类型

首先准备一个枚举类:

java
public enum WeekDay {
    MONDAY("周一"),
    TUESDAY("周二"),
    WEDNESDAY("周三"),
    THURSDAY("周四"),
    FRIDAY("周五"),
    SATURDAY("周六"),
    SUNDAY("周日");

    private String chineseName;

    private WeekDay(String chineseName){
        this.chineseName = chineseName;
    }

    public String toString(){
        return this.chineseName;
    }
}

然后在User中增加一个枚举属性:

java
private WeekDay weekDay;

4.4.1 序列化时枚举值默认使用name()方法

如果我们不做任何配置,序列化时枚举值会输出为name()值:

java
@Test
void test05() throws JsonProcessingException {
    User user = new User(1, "章三", null, false, WeekDay.MONDAY);

    ObjectMapper objectMapper = new ObjectMapper();
    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}
txt
{"id":1,"name":"章三","address":null,"adminFlag":false,"weekDay":"MONDAY"}

4.4.2 序列化时枚举值使用toString()方法

我们可以开启如下配置,使得序列化时枚举值使用toString()方法:

java
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
java
{"id":1,"name":"章三","address":null,"adminFlag":false,"weekDay":"周一"}

4.4.3 序列化时枚举值使用序号

java
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true);
txt
{"id":1,"name":"章三","address":null,"adminFlag":false,"weekDay":0}

注意:SerializationFeature.WRITE_ENUMS_USING_INDEX的优先级高于SerializationFeature.WRITE_ENUMS_USING_TO_STRING

4.4.4 反序列化时默认使用枚举值名称

java
@Test
void  test06() throws JsonProcessingException {
    String anotherJsonString = "{\"id\":1,\"name\":\"章三\",\"address\":null,\"adminFlag\":false,\"weekDay\":\"MONDAY\"}";

    ObjectMapper objectMapper = new ObjectMapper();
    User anotherUser = objectMapper.readValue(anotherJsonString, User.class);
    System.out.println(anotherUser);
}

4.4.5反序列化时默认使用toString()方法值

java
@Test
void  test06() throws JsonProcessingException {
    String anotherJsonString = "{\"id\":1,\"name\":\"章三\",\"address\":null,\"adminFlag\":false,\"weekDay\":\"周日\"}";

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
    User anotherUser = objectMapper.readValue(anotherJsonString, User.class);
    System.out.println(anotherUser);
}

4.4.6 不存在枚举值的情况

如果提供的json字符串无法反序列化为枚举值,默认情况下会报错,我们可以通过配置避免报错:

  • READ_UNKNOWN_ENUM_VALUES_AS_NULL:将未知枚举值置为null;

  • READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE:将未知枚举值置为默认枚举值。

    注意:这种方法需要配合注解@JsonEnumDefaultValue使用:

    java
    public enum WeekDay {
        @JsonEnumDefaultValue
        MONDAY("周一"),
        ...
    }
java
@Test
void  test06() throws JsonProcessingException {
    String anotherJsonString = "{\"id\":1,\"name\":\"章三\",\"address\":null,\"adminFlag\":false,\"weekDay\":\"未知\"}";

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
  	objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
    User anotherUser = objectMapper.readValue(anotherJsonString, User.class);
    System.out.println(anotherUser);
}

注意:READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE优先级高于READ_UNKNOWN_ENUM_VALUES_AS_NULL

5.自定义序列化器和反序列化器

如果我们的json字符串中的枚举值是序号,那要如何反序列化呢?Jackson并没有提供相关配置给我们,所以需要自己定义反序列化器,将序号转换为枚举值。

5.1 自定义反序列化器

要自定义反序列化器,需要我们继承StdDeserializer,实现deserialize方法将json值转换为属性:

java
public class WeekDayDeserializer extends StdDeserializer<WeekDay> {
    public WeekDayDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public WeekDay deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        int value = p.getIntValue();
        for (WeekDay weekDay : WeekDay.values()){
            if(weekDay.ordinal() == value)
                return weekDay;
        }

        return null;
    }
}

然后将自定义的反序列化器注册到ObjectMapper对象中:

java
@Test
void  test07() throws JsonProcessingException {
    String anotherJsonString = "{\"id\":1,\"name\":\"章三\",\"address\":null,\"adminFlag\":false,\"weekDay\":1}";

    ObjectMapper objectMapper = new ObjectMapper();

    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addDeserializer(WeekDay.class, new WeekDayDeserializer(WeekDay.class));
    objectMapper.registerModule(simpleModule);

    User anotherUser = objectMapper.readValue(anotherJsonString, User.class);
    System.out.println(anotherUser);
}

5.2 自定义序列化器

如果我们要自定义序列化器,需要继承StdSerializer,然后实现serialize方法,通过JsonGenerator将属性值写入json字符串中。

例如,在下面的例子中,我们将WeekDay枚举值序列化时输出为中文(英文)的形式。

java
public class WeekDaySerializer extends StdSerializer<WeekDay> {
    public WeekDaySerializer(Class<WeekDay> t) {
        super(t);
    }

    @Override
    public void serialize(WeekDay value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeString(String.format("%s(%s)", value.toString(), value.name()));
    }
}

然后在ObjectMapper中注册序列化器:

java
@Test
void test08() throws JsonProcessingException {
    User user = new User(1, "章三", null, false, WeekDay.MONDAY);

    ObjectMapper objectMapper = new ObjectMapper();

    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addSerializer(WeekDay.class, new WeekDaySerializer(WeekDay.class));
    objectMapper.registerModule(simpleModule);

    String jsonString = objectMapper.writeValueAsString(user);
    System.out.println(jsonString);
}

结果:

txt
{"id":1,"name":"章三","address":null,"adminFlag":false,"weekDay":"周一(MONDAY)"}

总结

本篇文章主要介绍了如何使用Jackson库中的ObjectMapper进行json序列化和反序列化,通过案例演示了相关配置以及自定义序列化器和反序列化器。