Appearance
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;
}然后使用ObjectMapper的writeValueAsString方法,将对象转换为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>>(){}方式,同理,Map、Set对象也类似处理。
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使用:javapublic 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序列化和反序列化,通过案例演示了相关配置以及自定义序列化器和反序列化器。