Jackson提供了一系列注解,可以使用这些注解来设置将JSON读入对象的方式或从对象生成什么JSON的方式,下面介绍一些常用的注解。
3.1 序列化
@JsonAnyGetter
@JsonAnyGetter 注解运行可以灵活的使用Map类型的作为属性字段,允许getter方法返回Map,该Map然后用于以与其他属性类似的方式序列化JSON的其他属性。通过序列化该实体Bean,我们将会得到Map属性中的所有Key作为属性值,测试序列化代码如下
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = new ObjectMapper().writeValueAsString(bean);
System.out.println(result);
}
}
class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
public ExtendableBean(String name) {
this.name = name;
this.properties = new HashMap<String, String>();
}
public void add(String key, String value) {
this.properties.put(key, value);
}
}
最终输出结果如下
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
如果不使用**@JsonAnyGetter**注解,那么最终序列化结果将会在properties属性下面,结果如下:
{
"name": "My bean",
"properties": {
"attr2": "val2",
"attr1": "val1"
}
}
@JsonRawValue
@JsonRawValue注解可以指定字符串属性类为json,如下代码:
import java.io.IOException;
import java.text.ParseException;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) throws IOException, ParseException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
System.out.println(result);
}
}
class RawBean {
public String name;
@JsonRawValue
public String json;
public RawBean(String name, String json) {
super();
this.name = name;
this.json = json;
}
}
最终使用@JsonRawValue序列化结果如下:
{
"name":"My bean",
"json":{
"attr":false
}
}
没有使用@JsonRawValue序列化结果如下:
{
"name":"My bean",
"json":"{\"attr\":false}"
}
@JsonRootName
@JsonRootName注解旨在给当前序列化的实体对象加一层包裹对象,允许在JSON上指定根节点。
@data
public class RootUser {
private String name;
private String title;
public RootUser(String name, String title) {
this.name = name;
this.title = title;
}
}
在上面的实体类中,正常情况下,如果要序列号RootUser对象,其结果格式为:
{
"name": "name1",
"title": "title1"
}
在RootUser加上**@JsonRootName** 注解后,该类改动如下:
@JsonRootName(value = "root")
public class RootUser {
private String name;
private String title;
public RootUser(String name, String title) {
this.name = name;
this.title = title;
}
}
@JsonRootName 仅用于指定 JSON 根属性的名称,只有当SerializationFeature.WRAP_ROOT_VALUE、DeserializationFeature.UNWRAP_ROOT_VALUE 启用时才有效。若只开启了 Feature 功能但未使用 @JsonRootName 注解,默认会使用类名作为 RootName。
ObjectMapper objectMapper=new ObjectMapper();
// 启用 SerializationFeature.WRAP_ROOT_VALUE
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
// 启用 DeserializationFeature.UNWRAP_ROOT_VALUE
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
String result=objectMapper.writeValueAsString(new RootUser("name1","title1"));
最终序列化JSON结果如下:
{
"root": {
"name": "name1",
"title": "title1"
}
}
@JsonSerialize
实际开发中,一定遇到过这样的问题:前端显示和后台存储数据单位不统一,而且各有各自的理由,统一不了,那就只能由后端转换。每次返回给前端时再转换一遍,返回给前端的json数据,在后端里定义的往往是一个对象,如何做到优雅的转换呢?例如,Double类型的数据返回值转换成字符串,只需两步操作:
- 写一个负责转换的类,里面写好规则。
public class CustomDoubleSerialize extends JsonSerializer<Double> {
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
BigDecimal d = new BigDecimal(value.toString());
gen.writeString(d.stripTrailingZeros().toPlainString());
}
}
- 在实体类上需要装换的字段上加上注解
/**
* 金额
*/
@JsonSerialize(using = CustomDoubleSerialize.class)
private Double amount;
又例如,假设某布尔值false和true,使其分别为0和1。
- 写一个负责转换的类,里面写好规则:将序列的真值序列化为1,将假值序列化为0。
public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {
@Override
public void serialize(Boolean aBoolean, JsonGenerator gen, SerializerProvider provider) throws IOException {
if(aBoolean){
jsonGenerator.writeNumber(1);
} else {
jsonGenerator.writeNumber(0);
}
}
}
- 在实体类上需要装换的字段上加上注解
@JsonSerialize(using = OptimizedBooleanSerializer.class)
public boolean enabled = false;
注:@JsonSerialize注解,主要应用于数据转换,该注解作用在该属性的getter方法上。
@JsonGetter
@JsonGetter 注解用于告诉Jackson,应该通过调用getter方法而不是通过直接字段访问来获取某个字段值。 如果您的Java类使用getter和setter名称,则@JsonGetter注解很有用。
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Student student = new Student("Mark", 1);
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);
System.out.println(jsonString);
}
}
class Student {
private String name;
private int rollNo;
public Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
@JsonGetter("studentName")
public String getName() {
return name;
}
public int getRollNo() {
return rollNo;
}
}
使用 @JsonGetter 输出如下结果:
{
"rollNo" : 1,
"studentName" : "Mark"
}
而没有使用 @JsonGetter 输出如下结果:
{
"name" : "Mark",
"rollNo" : 1
}
3.2 反序列化
@JsonCreator
@JsonCreator 用于告诉Jackson该Java对象具有一个构造函数,该构造函数可以将JSON对象的字段与Java对象的字段进行匹配。配合@JsonProperty注解能到达在反序列化实体对象时,指定不变更属性名称的效果。例如有如下JSON:
{
"id":1,
"theName":"My bean"
}
在实体类中,我们没有属性名称是theName,但我们想把theName属性反序列化时赋值给name,此时实体类对象结构如下:
public class BeanWithCreator {
public int id;
public String name;
@JsonCreator
public BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}
在BeanWithCreator的构造函数中添加@JsonCreator注解,并且配合@JsonProperty注解进行属性指向,最终反序列化代码如下:
import java.io.IOException;
import java.text.ParseException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) throws IOException, ParseException {
String json = "{\"id\":1,\"theName\":\"My bean\"}";
BeanWithCreator bean = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(json);
System.out.println(new ObjectMapper().writeValueAsString(bean));
}
}
class BeanWithCreator {
public int id;
public String name;
@JsonCreator
public BeanWithCreator(@JsonProperty("id") int id, @JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}
@JsonAnySetter
@JsonAnySetter 表示Jackson为JSON对象中所有无法识别的字段调用相同的setter方法, “无法识别”是指尚未映射到Java对象中的属性或设置方法的所有字段。@JsonAnySetter注解可以将来源JSON最终转化为Map类型的属性结构。
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) {
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"RollNo\" : \"1\",\"Name\" : \"Mark\"}";
try {
Student student = mapper.readerFor(Student.class).readValue(jsonString);
System.out.println(student.getProperties());
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Student {
private Map<String, String> properties;
public Student() {
properties = new HashMap<>();
}
public Map<String, String> getProperties() {
return properties;
}
@JsonAnySetter
public void add(String property, String value) {
properties.put(property, value);
}
}
@JsonSetter
@JsonSetter注解是@JsonProperty的替代注解,用于告诉Jackson将特定方法标记为setter方法。当将JSON读入对象时,应将此setter方法的名称与JSON数据中的属性名称匹配。当我们需要读取一些JSON数据时,但是目标实体类与该数据的属性名称不同,该注解是非常有用的。
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTester {
public static void main(String args[]) throws JsonProcessingException {
String json = "{\"id\":1,\"name\":\"My bean\"}";
ObjectMapper mapper = new ObjectMapper();
MyBean myBean = mapper.readerFor(MyBean.class).readValue(json);
System.out.println(mapper.writeValueAsString(myBean));
}
}
class MyBean {
private int id;
private String thename;
/**
* 通过指定setTheName作为属性name的setter方法,反序列化时可以达到最终效果
*/
@JsonSetter("name")
public void setTheName(String name) {
this.thename = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTheName() {
return thename;
}
}
@JsonDeserialize
@JsonDeserialize注解和序列化注解@JsonSerialize的效果是一致的,作用与反序列化时,针对特定的字段,存在差异化的发序列化效果。首先,需要将@JsonDeserialize注解添加到要为其使用自定义反序列化器的字段,如下所示
public class PersonDeserialize {
public long id = 0;
public String name = null;
@JsonDeserialize(using = OptimizedBooleanDeserializer.class)
public boolean enabled = false;
}
其次,这是@JsonDeserialize注解中引用的OptimizedBooleanDeserializer类的实例
public class OptimizedBooleanDeserializer
extends JsonDeserializer<Boolean> {
@Override
public Boolean deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws
IOException, JsonProcessingException {
String text = jsonParser.getText();
if("0".equals(text)) return false;
return true;
}
}
请注意,OptimizedBooleanDeserializer类使用通用类型Boolean扩展了JsonDeserializer。 这样做会使deserialize()方法返回一个布尔对象。 如果要反序列化其他类型(例如java.util.Date),则必须在泛型括号内指定该类型。
3.3 属性注解
@JsonIgnore
Jackson注解 @JsonIgnore 用来告诉 Jackson 在处理时忽略Java对象的某个属性(字段),在将JSON读取到Java对象中以及将Java对象写入JSON时,都将忽略该属性。
@Data
public class SellerInfoEntity {
private String id;
private String username;
private String password;
@JsonIgnore
private Timestamp createTime;
@JsonIgnore
private Timestamp updateTime;
public SellerInfoEntity() {
}
public SellerInfoEntity(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
}
使用注解前
{
"id":"1",
"username":"user1",
"password":"123456",
"createTime":null,
"updateTime":null
}
使用注解后,不会从JSON读取或写入JSON属性(createTime、updateTime)
{
"id":"1",
"username":"user1",
"password":"123456"
}
@JsonIgnoreProperties
与 @JsonIgnore 类似,都是告诉 Jackson 该忽略哪些属性,不同之处是 @JsonIgnoreProperties 是类级别的,放置在类声明上方,达到在序列化时忽略一个或多个字段的效果。
@Data
@JsonIgnoreProperties(value = {"createTime","updateTime"})
public class SellerInfoEntity {
private String id;
private String username;
private String password;
private String openid;
private Timestamp createTime;
private Timestamp updateTime;
public SellerInfoEntity() {
}
public SellerInfoEntity(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
}
在此示例中,属性createTime、updateTime都将被忽略,因为它们的名称在类声明上方的 @JsonIgnoreProperties 注解声明内列出。
在目标对象的类级别上添加注解:@JsonIgnoreProperties(ignoreUnknown = true),用于忽略字段不匹配情况。
@JsonInclude
使用@JsonInclude注解告诉Jackson仅在某些情况下包括属性。 例如,仅当属性为非null,非空或具有非默认值时,才应包括该属性。
@JsonInclude(Include.NON_NULL)
public class MyBean {
public int id;
public String name;
}
在MyBean中使用了Include.NON_NULL则代表该实体对象序列化时不会包含空值。
3.4 常规注解
@JsonProperty
类似于sql里字段的别名,可以指定某个属性和json映射的名称。当实体对象中没有标准的getter/setter方法时,我们可以使用该注解进行指定属性名称,方便进行序列化/反序列化。例如我们有个json字符串为{"user_name":"aaa"}
,而java中命名要遵循驼峰规则,则为userName,这时通过 @JsonProperty 注解来指定两者的映射规则即可。
public class SomeEntity {
@JsonProperty("user_name")
private String userName;
// ...
}
@JsonFormat
此注解用于属性上,针对日期字段可以通过使用@JsonFormat注解直接转化为指定的格式,例如 @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
。主要有下面几个常用的属性
属性 | 说明 |
shap | 表示序列化后的一种类型 |
pattern | 表示日期的格式 |
timezone | 若未标明时区,则默认为 GMT 时区,中国需要GMT+8 |
locale | 根据位置序列化的一种格式 |
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy年MM月dd日 HH:mm:ss", timezone = "GMT+8")
private Date date;
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
private Date date2;
@JsonFormat(locale = "zh_CN")
private Date date3;
跟 JsonSerializer 类似,如下所示:
@JsonSerialize(using = CustomDateSerializer.class)
private Date dateOfBirth;
class CustomDateSerializer extends JsonSerializer{
private static final long serialVersionUID = 1L;
private static SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
@Override
public void serialize(Date value,
JsonGenerator generator, SerializerProvider arg2) throws IOException {
generator.writeString(formatter.format(value));
}
}