一、序列化和反序列化
在SpringMVC中,前、后端之间数据传输会涉及到数据的序列化和反序列化操作。当注解为@ResponseBody时,请求响应默认使用的序列化方式是JSON,而SpringBoot默认是使用Jackson作为JSON数据格式处理的类库。
- 序列化:按照指定的格式、顺序等将实体类对象转换为JSON对象;
- 反序列化:将JSON对象中的字符串、数字,将其转换为包含Date类型、Integer等类型的实体对象;
二、在Spring中使用Jackson
Jackson提供了注解和手动数据转换两种方式,帮助我们进行序列化和反序列化工作。
2.1常用注解
注解通常用于标注java实体类或实体类的属性。
- @JsonPropertyOrder(value={"pname1","pname2"}) 改变子属性在JSON序列化中的默认定义的顺序。如:param1在先,param2在后。
- @JsonIgnore 加在属性上面,排除某个属性不做序列化与反序列化
- @JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在类上之后,就会忽略JSON字符串中存在,但实体类不存在的属性,不予赋值,也不会出现异常。
- @JsonIgnoreProperties({ "xxx", "yyyy" }) 忽略某些属性不进行序列化
- @JsonProperty(anotherName) 为某个属性换一个名称,体现在JSON数据里面
- @JsonInclude(JsonInclude.Include.NON_NULL) 排除为空的元素不做序列化反序列化
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 指定日期类型的属性格式
//因为定义了JsonPropertyOrder,content在先,title在后
@JsonPropertyOrder(value={"content","title"})
public class Article {
//因为定义了JsonIgnore,id属性被忽略
@JsonIgnore
private Long id;
//因为定义了JsonProperty,author属性变为authorName
@JsonProperty("authorName")
private String author;
private String title;
private String content;
//因为定义了JsonInclude和JsonFormat,createTime不能为空,并且格式为 "yyyy-MM-dd HH:mm:ss"
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
private List<Reader> reader;
}
上文代码中对应的JSON数据格式可以为:
{
authorName:"",
content:"",
title:"",
createTime:"2023-06-08 17:12:12",
reader:[{"name":"William","age":26},{"name":"Jerry","age":37}]
}
通常会对日期类型转换,进行全局配置,而不是在每一个java bean里面配置
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
2.2手动数据转换
除了在spring框架内实现自动的前后端JSON数据与java对象的转换,我们还可以使用jackson自己写代码进行转换。
//jackson的ObjectMapper 转换对象
ObjectMapper mapper = new ObjectMapper();
//将某个java对象转换为JSON字符串
String jsonStr = mapper.writeValueAsString(javaObj);
//将jsonStr转换为Article类的对象
Article article = mapper.readValue(jsonStr, Article.class);
当JSON字符串代表的对象的字段多于类定义的字段时,使用readValue会抛出UnrecognizedPropertyException异常,在类的定义处加上@JsonIgnoreProperties(ignoreUnknown = true)可以解决这个问题。
三、Jackson全局配置
在Spring框架内使用Jackson的时候,通常需要一些特殊的全局配置,来应对我们JSON序列化与反序列化中出现的各种问题。Spring Boot 提供了两种配置方式:配置文件的方式和代码的方式。配置文件的方式更容易,单代码的方式二更灵活。当配置文件的方式无法解决的问题,可以尝试使用代码的方式进行配置。
2.1配置文件的方式
spring:
jackson:
#日期类型格式化
date-format: yyyy-MM-dd HH:mm:ss
serialization:
#格式化输出,通常为了节省网络流量设置为false。因为格式化之后会带有缩进,方便阅读。
indent_output: false
#某些类对象无法序列化的时候,是否报错
fail_on_empty_beans: false
#设置空如何序列化,见下文代码方式详解
defaultPropertyInclusion: NON_EMPTY
deserialization:
#json对象中有不存在的属性时候,是否报错
fail_on_unknown_properties: false
parser:
#允许出现特殊字符和转义符
allow_unquoted_control_chars: true
#允许出现单引号
allow_single_quotes: true
2.2代码的方式
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
// Include.Include.ALWAYS 默认
// Include.NON_DEFAULT 属性为默认值不序列化
// Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
// Include.NON_NULL 属性为NULL 不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 允许出现特殊字符和转义符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
// 允许出现单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 字段保留,将null值转为""
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){
@Override
public void serialize(Object o, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)throws IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}