前言

平日里项目中处理JSON一般用的都是阿里巴巴的fastjson,现在发现使用SpringBoot内置的Jackson的序列化和反序列化也挺方便的。Jackson不但可以完成简单的序列化和反序列化操作,也能实现复杂的个性化的序列化和反序列化操作。

——以上文案皆盗于鸟叔博客,勿怪勿怪。

准备

实体类User

1
2
3
4
5
6
7
8
9
10
11
12
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;

private Integer id;
private String name;
private Date birth;

// NoArgsConstructor
// AllArgsConstructor
// Getter and Setter
// ToString
}

ObjectMapper API

序列化与反序列化

序列化属性SerializationFeature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
WRAP_ROOT_VALUE(false),                   
INDENT_OUTPUT(false),
FAIL_ON_EMPTY_BEANS(true),
FAIL_ON_SELF_REFERENCES(true),
WRAP_EXCEPTIONS(true),
FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS(true),
CLOSE_CLOSEABLE(false),
FLUSH_AFTER_WRITE_VALUE(true),
WRITE_DATES_AS_TIMESTAMPS(true),
WRITE_DATE_KEYS_AS_TIMESTAMPS(false),
WRITE_DATES_WITH_ZONE_ID(false),
WRITE_DURATIONS_AS_TIMESTAMPS(true),
WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),
WRITE_ENUMS_USING_TO_STRING(false),
WRITE_ENUMS_USING_INDEX(false),
WRITE_ENUM_KEYS_USING_INDEX(false),
WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false),
WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
ORDER_MAP_ENTRIES_BY_KEYS(false),
EAGER_SERIALIZER_FETCH(true),
USE_EQUALITY_FOR_OBJECT_ID(false);

反序列化属性DeserializationFeature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
USE_BIG_DECIMAL_FOR_FLOATS(false),
USE_BIG_INTEGER_FOR_INTS(false),
USE_LONG_FOR_INTS(false),
USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),
FAIL_ON_UNKNOWN_PROPERTIES(true),
FAIL_ON_NULL_FOR_PRIMITIVES(false),
FAIL_ON_NUMBERS_FOR_ENUMS(false),
FAIL_ON_INVALID_SUBTYPE(true),
FAIL_ON_READING_DUP_TREE_KEY(false),
FAIL_ON_IGNORED_PROPERTIES(false),
FAIL_ON_UNRESOLVED_OBJECT_IDS(true),
FAIL_ON_MISSING_CREATOR_PROPERTIES(false),
FAIL_ON_NULL_CREATOR_PROPERTIES(false),
FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true),
FAIL_ON_TRAILING_TOKENS(false),
WRAP_EXCEPTIONS(true),
ACCEPT_SINGLE_VALUE_AS_ARRAY(false),
UNWRAP_SINGLE_VALUE_ARRAYS(false),
UNWRAP_ROOT_VALUE(false),
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false),
ACCEPT_FLOAT_AS_INT(true),
READ_ENUMS_USING_TO_STRING(false),
READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),
READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true),
EAGER_DESERIALIZER_FETCH(true);

environment配置

可配置

  • spring.jackson.deserialization.=true|false
  • spring.jackson.generator.=true|false
  • spring.jackson.mapper.=true|false
  • spring.jackson.parser.=true|false
  • spring.jackson.serialization.=true|false
  • spring.jackson.serialization-inclusion=always|non_null|non_absent|non_default|non_empty

application.yml:

1
2
3
4
5
6
7
8
9
10
11
12
spring:
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
# 设置空属性何如序列化
default-property-inclusion: non_empty
# 序列化
serialization:
# 反序列化
deserialization:
# 解析
parser:

configuration配置

在@Configuration类中生成bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Configuration
public class JacksonConfig {

@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 序列化日期格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
// 没有匹配的属性名称时不作处理
objectMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);

// 序列化
//禁止序列化空值
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
objectMapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, true);
// 不包含空值属性
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

// 反序列化
//禁止遇到空原始类型时抛出异常,用默认值代替
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
// 禁止遇到未知(新)属性时报错,支持兼容扩展
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
objectMapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);

return objectMapper;
}
}

Rest接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController
@RequestMapping("/user")
public class UserController {
private static Logger log = LoggerFactory.getLogger(UserController.class);

@Autowired
private ObjectMapper objectMapper;

// 序列化接口
@GetMapping("/get")
public String getUser() throws JsonProcessingException {
User user = new User(1, "KHighness", new Date());
return objectMapper.writeValueAsString(user);
}

// 反序列化接口
@PostMapping("/save")
public void saveUser(@RequestBody String userJsonList) throws IOException {
List<User> userList = objectMapper.readValue(userJsonList, new TypeReference<List<User>>() {});
userList.forEach(e -> {log.info(e.toString());});
}
}

CURL测试(windows下建议使用cmd进行测试,用powershell会到导致POST错误)
序列化测试:

1
$ curl -X GET http://localhost:3333/user/get

序列化测试结果:

1
{"id":1,"name":"KHighness","birth":"2021-04-15"}

反序列化测试:

1
$ curl -H "Content-Type:application/json" -X POST --data "[{\"id\":1, \"name\":\"Khighness\", \"birth\":\"2001-09-11\"}, {\"id\":2, \"name\":\"FlowerK\", \"birth\":\"2003-07-24\"}]"  http://localhost:3333/user/save

反序列化测试结果:

1
[{"id":1,"name":"Khighness","birth":"2001-09-11"},{"id":2,"name":"FlowerK","birth":"2003-07-24"}]

Jackson注解

(1)@JsonProperty
作用在属性上,用于序列化和反序列化时为JSON key指定一个别名。
例如:

1
2
@JsonProperty("bth")
private Date birth;

此时序列化测试结果为:

1
{"id":1,"name":"KHighness","bth":"2021-04-16"}

(2)@JsonIgnore
作用在属性上,用于在序列化和反序列化时忽略此属性。
例如:

1
2
@JsonIgnore
private String name;

此时序列化测试结果为:

1
{"id":1,"birth":"2021-04-16"}

(3)@JsonIgnoreProperties
作用在类上,用于忽略一组属性。
例如:

1
@JsonIgnoreProperties({"id", "birth"})

此时序列化测试结果为:

1
{"name":"KHighness"}

(4)@JsonFormat
作用在日期属性上,用于格式化。
例如:

1
2
@JsonFormat(pattern = "yyyy.MM.dd")
private Date birth;

此时序列化测试结果为:

1
{"id":1,"userName":"KHighness","birth":"2021.04.16"}

(5)@JsonNaming
作用在类上,用于指定一个命名策略。

Jackson自带了五种(两种)命名策略,使用方式,

1
2
3
4
@JsonNaming(PropertyNamingStrategy.<Strategy>.class)
public class User {
private String userName;
}
命名策略 中文描述 作用结果
KebabCaseStrategy 中划线 user-name
SnakeCaseStrategy 下划线 user_name
UpperCamelCaseStrategy 大驼峰 UserName
LowerCaseStrategy 全小写 username
LowerDotCaseStrategy 小写点 user.name

(6)@JsonSerialize
作用在类上,指定一个类来自定义序列化,该类必须实现JsonSerializer接口。
例如:

1
2
3
4
5
6
7
8
9
public class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("USER-NAME", user.getUserName());
jsonGenerator.writeEndObject();
}
}
1
2
3
4
5
6
@JsonSerialize(using = UserSerializer.class)
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;
private String userName;
// ...
}

此时序列化接口测试结果为:

1
{"USER-NAME":"KHighness"}

(7)@JsonDeserialize
作用在类上,指定一个类来自定义反序列化,该类必须实现JsonDeserializer接口。
例如:

1
2
3
4
5
6
7
8
9
10
11
public class UserDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String userName = node.get("user-name").asText();
User user = new User();
user.setUserName(userName);
return user;
}
}
1
2
3
4
5
6
@JsonDeserialize(using = UserDeserializer.class)
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;
private String userName;
// ...
}

反序列化测试:

1
2
curl -H "Content-Type:application/json" -X POST --data "[{\"user-name\":\"Khighness\"}, {\"user-name\":\"FlowerK\"}]"  http://localhost:3333/user/save
[{"userName":"Khighness"},{"userName":"FlowerK"}]

反序列化测试结果为:

1
[{"userName":"Khighness"},{"userName":"FlowerK"}]

(8)@JsonView
作用在类、属性和方法上,用来序列化组。
比如对于User对象,某些情况下只返回userName即可,

而某些情况下需要返回全部属性。因此User对象可以这样定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;

// 仅包含userName
public interface UserNameView {};

// 包含全部属性
public interface AllUserFieldView extends UserNameView {};

@JsonView(AllUserFieldView.class)
private Integer id;

@JsonView(UserNameView.class)
private String userName;

@JsonView(AllUserFieldView.class)
private Date birth;

// ...
}

然后在controller的方法上使用@JsonView,可以指定序列化组名。
使用组名UserNameView:

1
2
3
4
5
6
@JsonView(User.UserNameView.class)
@GetMapping("/get")
public User getUser() throws JsonProcessingException {
User user = new User(1, "KHighness", new Date());
return user;
}

序列化测试结果为:

1
{"userName":"KHighness"}

当组名指定为AllUserFieldView时,序列化测试结果为:

1
{"id":1,"userName":"KHighness","birth":"2021-04-17"}

参考
[1] 🐦 SpringBoot中的JSON技术: https://mrbird.cc/Spring-Boot%20JSON.html
[2] ☁️ 自定义Jackson ObjectMapper:https://www.kancloud.cn/ahutchen/spring-boot-reference-guide/333370