Jackson作为springMVC默认的MessageConverter(消息序列化工具),经常在项目中使用,如果熟悉Jackson常用的使用方法,特性化机制,就会事半功倍,极大提高前后端数据交互的灵活性
maven依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
程序包还依赖jackson-core和jackson-annotations,但是当使用Maven或Gradle之类的构建工具时,会自动包含依赖项。但是,你可能想使用jackson-bom来确保兼容版本的依赖项。 如果未使用可使用项目的pom.xml处理依赖项的构建工具,则需要下载并明确包含这两个jar。
jackson-databind
官网文档地址https://github.com/FasterXML/jackson-databind
常用注解
@JsonSerialize 用于序列化
注解用于配置序列化,用在“ getter”方法、属性字段,用于指定序列化时使用的自定义JsonSerialize类
我们定义一个Vo
public class JackSonVo {
private String userName;
@JsonSerialize(using = JsonTimeStampSerializer.class)
private Timestamp createTime;
@JsonSerialize(using = JsonDateSerializer.class)
private Date beginDate;
@JsonSerialize(using = JsonDoubleSerializer.class)
private Double score;
public JackSonVo(String userName, Double score) {
super();
this.userName = userName;
this.score = score;
}
}
public class JsonTimeStampSerializer extends JsonSerializer<Timestamp> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void serialize(final Timestamp timestamp, final JsonGenerator gen, final SerializerProvider provider) throws IOException,
JsonProcessingException {
String value = dateFormat.format(timestamp);
gen.writeString(value);
}
}
public class JsonDateSerializer extends JsonSerializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(final Date date, final JsonGenerator gen, final SerializerProvider provider) throws IOException, JsonProcessingException {
String value = dateFormat.format(date);
gen.writeString(value);
}
}
public class JsonDoubleSerializer extends JsonSerializer<Double> {
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
if (value != null)
gen.writeString(BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_UP).toString()); // ROUND_HALF_UP四舍五入
}
}
测试用例
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo("prince", new Timestamp(System.currentTimeMillis()), new Date(), 5.678);
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println(jsonString);
测试结果
在Vo属性字段上使用@JsonSerialize前
{"userName":"prince","createTime":1577431715921,"beginDate":1577431715921,"score":5.678}
在Vo属性字段上使用@JsonSerialize后
{"userName":"prince","createTime":"2019-12-27 15:25:41","beginDate":"2019-12-27","score":"5.68"}
@JsonProperty 用于序列化和反序列化
用在属性或者getter/setter方法上面,用于改变序列化/反序列化时字段的名称
我们在userName属性字段上加上该注解
@JsonProperty("user_name")
private String userName;
序列化结果(注意看userName序列化时的key)
{"createTime":"2019-12-27 15:42:48","beginDate":"2019-12-27","score":"5.68","user_name":"prince"}
反序列化测试用例及结果
// 反序列化
JackSonVo jackSonVoDes = mapper.readValue(
"{\"createTime\":\"2019-12-27 15:42:48\",\"beginDate\":\"2019-12-27\",\"score\":\"5.68\",\"user_name\":\"prince\"}",
JackSonVo.class);
System.out.print(jackSonVoDes);
JackSonVo [userName=prince, createTime=2019-12-27 15:42:48.0, beginDate=Fri Dec 27 00:00:00 CST 2019, score=5.68]
注意:上面反序列化能够正常执行,还需要在createTime上加上@JsonFormat这个注解,否则会报错 Can not deserialize value of type java.sql.Timestamp from String "2019-12-27 15:42:48":
@JsonFormat 用于序列化和反序列化
通用注解,用于配置有关属性的值如何序列化的详细信息。 与大多数其他Jackson注解不同,该注解没有特定的通用解释:相反,效果取决于要注解的属性的数据类型(或更具体地说,使用的是反序列化器和序列化器)。
如,在上面的反序列化的例子中,我们需要给createTime加上如下的注解才能将其从json字符串格式反序列化到对象的Timestamp类型
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp createTime;
在序列化时,该注解的作用就是将值序列化成我们需要的格式,这次我们在时间上不使用@JsonSerialize ,而只使用@JsonFormat来测试时间的序列化和反序列化(我们先使用@JsonIgnore将userName和score忽略掉)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp createTime;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date beginDate;
测试用例
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo(new Timestamp(System.currentTimeMillis()), new Date());
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println(jsonString);
// 反序列化
JackSonVo jackSonVoDes = mapper.readValue(
"{\"createTime\":\"2019-12-27 15:42:48\",\"beginDate\":\"2019-12-27\"}",
JackSonVo.class);
System.out.print(jackSonVoDes);
运行结果
{"createTime":"2019-12-27 16:14:18","beginDate":"2019-12-27"}
JackSonVo [createTime=2019-12-27 15:42:48.0, beginDate=Fri Dec 27 00:00:00 CST 2019]
@JsonIgnore 用于序列化和反序列化,用在getter、setter、属性上面,用于序列化/反序列化时忽略该属性
另外,从Jackson 1.9开始,如果这是与属性关联的唯一注释,则还将导致整个属性被忽略:也就是说,如果setter具有此注释,而getter没有注释,则getter也将被有效忽略。 不同的访问者仍然可以使用不同的注释。 因此,如果仅忽略“ getter”,则其他访问器(setter或字段)将需要显式注释以防止被忽略。
在这个注解的例子中,我们将Vo只用userName和score这两个属性,如下
public class JackSonVo {
@JsonIgnore
private String userName;
private Double score;
public JackSonVo() {
super();
}
public JackSonVo(String userName, Double score) {
super();
this.userName = userName;
this.score = score;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public String toString() {
return "JackSonVo [userName=" + userName + ", score=" + score + "]";
}
}
测试用例代码
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo("prince", 5.678);
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println("序列化:" + jsonString);
// 反序列化
JackSonVo jackSonVoDes = mapper.readValue("{\"userName\":\"prince\",\"score\":\"5.68\"}", JackSonVo.class);
System.out.print("反序列化:" + jackSonVoDes);
测试结果
序列化:{"score":5.678}
反序列化:JackSonVo [userName=null, score=5.68]
我们发现,在属性上使用@JsonIgnore时,在序列化和反序列化时,都忽略了这个字段。注意上面的说明,从1.9开始,我们在setter上添加该注解,会导致getter上也忽略该属性,同样在getter上添加该注解,也会导致setter上忽略该属性;如果不想要忽略,则需要明确指明,如我们在userName的setter上使用@JsonSetter(1.5版本以后该注解过时,推荐使用后者)或者@JsonProperty,在getter上使用@JsonIgnore,如下。
public class JackSonVo {
private String userName;
private Double score;
public JackSonVo() {
super();
}
public JackSonVo(String userName, Double score) {
super();
this.userName = userName;
this.score = score;
}
@JsonIgnore
public String getUserName() {
return userName;
}
@JsonSetter //或者@JsonProperty
public void setUserName(String userName) {
this.userName = userName;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public String toString() {
return "JackSonVo [userName=" + userName + ", score=" + score + "]";
}
}
再次执行上面测试用例得到如下结果
序列化:{"score":5.678}
反序列化:JackSonVo [userName=prince, score=5.68]
@JsonInclude 用于序列化
当被注解的属性或者被注解的类的全部属性序列化的时候使用。没有被注解的属性值是包含的,但是通过该注解指明的不包含规则以减少要写出的属性数量。
现在我们对Vo使用该注解
@JsonInclude(Include.NON_NULL)
public class JackSonVo {
private String userName;
private Double score;
public JackSonVo() {
super();
}
public JackSonVo(String userName, Double score) {
super();
this.userName = userName;
this.score = score;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public String toString() {
return "JackSonVo [userName=" + userName + ", score=" + score + "]";
}
}
我们写测试用例进行序列化(注意:我们将userNmae的值设置成null)
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo(null, 5.678);
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println("序列化:" + jsonString);
测试结果如下,因为userName为null,所以序列化的时候就忽略了该属性
序列化:{"score":5.678}
@JsonPropertyOrder 用于序列化,作用于类
用于定义序列化对象属性时使用的排序(可能是部分)。 注解声明中包含的属性将首先进行序列化(按定义的顺序),然后是定义中未包含的所有属性。 注解定义将覆盖所有隐式顺序(例如,确保创建者属性在非创建者属性之前被序列化)
JsonPropertyOrder 支持alphabetic(按照字母顺序排序),默认alphabetic=false
这次我们修改Vo为
@Data
public class JackSonVo {
private String userName;
private Double score;
private Integer age;
public JackSonVo(String userName, Double score, Integer age) {
super();
this.userName = userName;
this.score = score;
this.age = age;
}
}
执行测试用例代码
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo("prince", 5.678, 30);
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println("序列化:" + jsonString);
使用该注解前
序列化:{"userName":"prince","score":5.678,"age":30}
我们在VO上使用该注解并定义序列化属性的顺序
@JsonPropertyOrder({ "age", "userName" })
执行结果
序列化:{"age":30,"userName":"prince","score":5.678}
@JsonRawValue
指示应按原样包含属性的文字String值(而不用引号)来序列化被注释的方法或字段。 这对于注入已在JSON中序列化的值或将javascript函数定义从服务器传递到javascript客户端很有用。
修改Vo
@Data
public class JackSonVo {
private String userName;
private Double score;
private Integer age;
private String json;
public JackSonVo(String userName, Double score, Integer age, String json) {
super();
this.userName = userName;
this.score = score;
this.age = age;
this.json = json;
}
}
执行测试用例
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo("prince", 5.678, 30, "{\"address\":\"china\"}");
String jsonString = mapper.writeValueAsString(jackSonVo);
System.out.println("序列化:" + jsonString);
使用该注解前结果
序列化:{"userName":"prince","score":5.678,"age":30,"json":"{\"address\":\"china\"}"}
在json字段上加上该注解后,再次执行测试用例,输出结果如下
序列化:{"userName":"prince","score":5.678,"age":30,"json":{"address":"china"}}
请注意,json的值的不同
@JsonRootName用于序列化和反序列化
作用在类上,指定序列化时根包装的名称。序列化需要与SerializationFeature.WRAP_ROOT_VALUE配合使用,反序列化需要与DeserializationFeature.UNWRAP_ROOT_VALUE配合使用
Vo
@Data
@JsonRootName("root")
public class JackSonVo {
private String userName;
private Double score;
private Integer age;
public JackSonVo(String userName, Double score, Integer age) {
super();
this.userName = userName;
this.score = score;
this.age = age;
}
public JackSonVo() {
super();
}
}
测试用例及执行结果
ObjectMapper mapper = new ObjectMapper();
JackSonVo jackSonVo = new JackSonVo("prince", 5.678, 30);
String jsonString = mapper.enable(SerializationFeature.WRAP_ROOT_VALUE).writeValueAsString(jackSonVo);
System.out.println("序列化:" + jsonString);
// 反序列化
JackSonVo jackSonVoDes = mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE)
.readValue("{\"root\":{\"userName\":\"prince\",\"score\":5.678,\"age\":30}}", JackSonVo.class);
System.out.print("反序列化:" + jackSonVoDes);
序列化:{"root":{"userName":"prince","score":5.678,"age":30}}
反序列化:JackSonVo(userName=prince, score=5.678, age=30)
@JsonCreator用于反序列化,用在构造器、工厂方法上,用来初始化关联的对象
当json在反序列化时,默认选择类的无参构造函数创建类对象,没有无参构造函数时会报错(在前面的测试用例中,已经经历到了有的vo是需要有无参构造函数的,否则会报错can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
@JsonCreator作用就是指定一个有参构造函数供反序列化时调用。
该构造方法的参数前面需要加上@JsonProperty,否则会报错。
我们的VO如下:
注意:如果有多个参数的话,@JsonProperty必须显示指明属性的名字如下
@Data
public class JackSonVo {
private String userName;
private Double score;
private Integer age;
@JsonCreator
public JackSonVo(@JsonProperty("userName") String userName, @JsonProperty("score") Double score,
@JsonProperty("age") Integer age) {
super();
this.userName = userName;
this.score = score;
this.age = age;
}
}
测试用例代码
ObjectMapper mapper = new ObjectMapper();
// 反序列化
JackSonVo jackSonVoDes = mapper.readValue("{\"userName\":\"prince\",\"score\":5.678,\"age\":30}",
JackSonVo.class);
System.out.print("反序列化:" + jackSonVoDes);
测试结果
反序列化:JackSonVo(userName=prince, score=5.678, age=30)
其他注解
@JsonDeserialize
@JsonGetter
@JsonValue
@JsonUnwrapped
@JsonTypeInfo 等
Jackson和SpringMVC整合使用
jackson作为springMVC官方推荐使用MessageConverter工具,并成为springMVC默认的MessageConverter工具,因此在springMVC中使用jackson非常简单,springMVC会在需要json格式的response响应时,扫描项目中是否拥有jackson的相关类库,如果拥有,那么默认就会使用jackson进行消息转换,如果没有,就会报异常。无需手动配置jackson。
如果需要个性化的配置,就需要对MappingJackson2HttpMessageConverter进行设置,例如对jackson时间格式的输出进行全局修改,进行如下配置
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<constructor-arg name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg index="0" value="yyyy-MM-dd HH:mm:ss" />
<constructor-arg index="1" value="#{T(java.util.Locale).SIMPLIFIED_CHINESE}" />
<property name="timeZone" value="GMT+8" />
</bean>
</property>
</bean>
</constructor-arg>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>