前言
平日里项目中处理JSON一般用的都是阿里巴巴的fastjson,现在发现使用SpringBoot内置的Jackson的序列化和反序列化也挺方便的。Jackson不但可以完成简单的序列化和反序列化操作,也能实现复杂的个性化的序列化和反序列化操作。
——以上文案皆盗于鸟叔博客,勿怪勿怪。
准备
实体类User
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
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
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:
spring:
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
# 设置空属性何如序列化
default-property-inclusion: non_empty
# 序列化
serialization:
# 反序列化
deserialization:
# 解析
parser:
configuration配置
在@Configuration类中生成bean:
@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接口:
@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错误)
序列化测试:
$ curl -X GET http://localhost:3333/user/get
序列化测试结果:
{"id":1,"name":"KHighness","birth":"2021-04-15"}
反序列化测试:
$ 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
反序列化测试结果:
[{"id":1,"name":"Khighness","birth":"2001-09-11"},{"id":2,"name":"FlowerK","birth":"2003-07-24"}]
Jackson注解
(1)@JsonProperty
作用在属性上,用于序列化和反序列化时为JSON key指定一个别名。
例如:
@JsonProperty("bth")
private Date birth;
此时序列化测试结果为:
{"id":1,"name":"KHighness","bth":"2021-04-16"}
(2)@JsonIgnore
作用在属性上,用于在序列化和反序列化时忽略此属性。
例如:
@JsonIgnore
private String name;
此时序列化测试结果为:
{"id":1,"birth":"2021-04-16"}
(3)@JsonIgnoreProperties
作用在类上,用于忽略一组属性。
例如:
@JsonIgnoreProperties({"id", "birth"})
此时序列化测试结果为:
{"name":"KHighness"}
(4)@JsonFormat
作用在日期属性上,用于格式化。
例如:
@JsonFormat(pattern = "yyyy.MM.dd")
private Date birth;
此时序列化测试结果为:
{"id":1,"userName":"KHighness","birth":"2021.04.16"}
(5)@JsonNaming
作用在类上,用于指定一个命名策略。
Jackson自带了五种(两种)命名策略,使用方式,
@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
接口。
例如:
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();
}
}
@JsonSerialize(using = UserSerializer.class)
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;
private String userName;
// ...
}
此时序列化接口测试结果为:
{"USER-NAME":"KHighness"}
(7)@JsonDeserialize
作用在类上,指定一个类来自定义反序列化,该类必须实现JsonDeserializer
接口。
例如:
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;
}
}
@JsonDeserialize(using = UserDeserializer.class)
public class User implements Serializable {
private static final long serialVersionUID = -3180230416244251692L;
private String userName;
// ...
}
反序列化测试:
curl -H "Content-Type:application/json" -X POST --data "[{\"user-name\":\"Khighness\"}, {\"user-name\":\"FlowerK\"}]" http://localhost:3333/user/save
[{"userName":"Khighness"},{"userName":"FlowerK"}]
反序列化测试结果为:
[{"userName":"Khighness"},{"userName":"FlowerK"}]
(8)@JsonView
作用在类、属性和方法上,用来序列化组。
比如对于User对象,某些情况下只返回userName即可,
而某些情况下需要返回全部属性。因此User对象可以这样定义:
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:
@JsonView(User.UserNameView.class)
@GetMapping("/get")
public User getUser() throws JsonProcessingException {
User user = new User(1, "KHighness", new Date());
return user;
}
序列化测试结果为:
{"userName":"KHighness"}
当组名指定为AllUserFieldView
时,序列化测试结果为:
{"id":1,"userName":"KHighness","birth":"2021-04-17"}
参考
[1] 🐦 SpringBoot中的JSON技术
[2] ☁️ 自定义Jackson ObjectMapper
[