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>