本篇文章参考于简书:https://www.jianshu.com/p/4bd355715419,在此基础上验证并稍作修改。
老版本的Jackson使用的包名为org.codehaus.jackson
,而新版本使用的是com.fasterxml.jackson
。
Jackson主要包含了3个模块:
Jackson有三种方式处理Json:
通常来说,我们在日常开发中使用的是第3种方式,有时为了简便也会使用第2种方式,比如你要从一个很大的Json对象中只读取那么一两个字段的时候,采用databind方式显得有些重,JsonNode反而更简单。
使用ObjectMapper
本文的例子以jackson-databind-2.8.1为例。
创建User类如下:
public class User {
private String name;
private int age;
private int sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
序列化一个User对象:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setName("123");
user.setAge(12);
user.setSex(1);
System.out.println(objectMapper.writeValueAsString(user));
}
返回结果:
{"name":"123","age":12,"sex":1}
在默认情况下,ObjectMapper在序列化时,将所有的字段一一序列化,无论这些字段是否有值,或者为null。
另外,序列化依赖于getter方法,如果某个字段没有getter方法,那么该字段是不会被序列化的。由此可见,在序列化时,OjbectMapper是通过反射机制找到了对应的getter,然后将getter方法对应的字段序列化到Json中。请注意,此时ObjectMapper并不真正地检查getter对应的属性是否存在于User对象上,而是通过getter的命名规约进行调用,比如对于getAbc()方法:
public String getAbc(){
return "this is abc";
}
即便User上没有abc属性,abc也会被序列化:
{"name":"123","age":12,"sex":1,"abc":"this is abc"}
反序列化一个Json字符串,其中少了一个name字段:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue("{\"age\":12,\"sex\":1}", User.class);
System.out.println("name: " + user.getName());
System.out.println("age: " + user.getAge());
System.out.println("sex: " + user.getSex());
}
输出:
name: null
age: 12
sex: 1
可以看出,少了name字段,程序依然正常工作,只是name的值为null。
另外,如果我们向Json中增加一个User中没有的字段:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue("{\"name\":\"123\",\"age\":12,\"sex\":1,\"abc\":\"this is abc\"}", User.class);
System.out.println("name: " + user.getName());
System.out.println("age: " + user.getAge());
System.out.println("sex: " + user.getSex());
}
此时运行程序将报错:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "abc" (class com.study.common.User), not marked as ignorable (3 known properties: "name", "sex", "age"])
at [Source: (String)"{"name":"123","age":12,"sex":1,"abc":"this is abc"}"; line: 1, column: 39] (through reference chain: com.study.common.User["abc"])
表示在User对象上找不到对应的abc属性,但是如果我们在User上增加一个空的setter:
public void setAbc(String abc) {
}
那么此时运行成功,由此可见OjbectMapper是通过反射的机制,通过调用Json中字段所对应的setter方法进行反序列化的。并且此时,依赖于User上有默认构造函数。
综上,在默认情况下(即不对ObjectMapper做任何额外配置,也不对Java对象加任何Annotation),ObjectMapper依赖于Java对象的默认的无参构造函数进行反序列化,并且严格地通过getter和setter的命名规约进行序列化和反序列化。
去除getter和setter
纯粹地为了技术方面的原因而添加getter和setter是不好的,可以通过以下方式去除掉对getter和setter的依赖:
objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
ObjectMapper将通过反射机制直接操作Java对象上的字段。
此时创建User如下:
public class User {
private String name;
private int age;
private int sex;
public User(String name, int age, int sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
}
序列化User:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
User user = new User("name", 12, 1);
System.out.println(objectMapper.writeValueAsString(user));
}
然而,此时反序列化的时候报错:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.study.common.User: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: (String)"{"name":"name","age":12,"sex":1}"; line: 1, column: 2]
这是因为ObjectMapper在为字段设值之前,无法初始化User对象,此时有两种解决方式:
public User() {
}
@JsonCreator
注解,通常与@JsonProperty
一起使用: @JsonCreator
public User(@JsonProperty("name")String name, @JsonProperty("age")int age, @JsonProperty("sex")int sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
忽略字段
@JsonIgnore
用于字段上,表示该字段在序列化和反序列化的时候都将被忽略。
@JsonIgnoreProperties
主要用于类上:
@JsonIgnoreProperties(value = {"age","sex"},ignoreUnknown = true)
表示对于age和sex字段,反序列化和序列化均忽略,而对于Json中存在的未知字段,在反序列化时忽略,ignoreUnknown不对序列化起效。
序列化时排除null或者空字符串
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
表示在序列化User时,将值为null的字段排除掉。
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class User {
表示在序列化User时,将值为null的字段或者空的字符串字段排除掉。
用某个方法的返回值序列化整个对象
有时我们并不想讲对象的所有字段都序列化,而是希望用一个方法的返回值来序列化,比如toString()方法,此时可以用@JsonValue
:
@JsonValue
public String toString(){
return "someValue";
}
此时整个对象在序列化后的值将变为“someValue”。
反序列化时陈列多余字段
在默认情况下,如果Json数据中有多余的字段,那么在反序列化时Jackson发现无法找到对应的对象字段,便会抛出UnrecognizedPropertyException: Unrecognized field xxx
异常,此时可以做如下配置:
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectMapper配置:
ObjectMapper objectMapper = new ObjectMapper();
//去掉默认的时间戳格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为东八区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 设置输入:禁止把POJO中值为null的字段映射到json字符串中
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
//空值不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//序列化日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//序列化枚举是以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
//序列化枚举是以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);
//类为空时,不要抛异常
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//反序列化时,遇到未知属性时是否引起结果失败
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//单引号处理
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
//解析器支持解析结束符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
平时用的配置如下:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules()
.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
另外,添加对Java 8的支持,在pom.xml
文件中加入依赖:
com.fasterxml.jackson.core
jackson-core
2.9.0
com.fasterxml.jackson.core
jackson-databind
2.8.1
以上配置有以下几点:
objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
@JsonCreator