一. 前言
jackson 1.x和2.x版本的注解是放置在不同的包下的
1.x是在jackson core jar包org.codehaus.jackson.annotate下
2.x是在jackson-databind包com.fasterxml.jackson.annotation下
本页面列出常规用途的Jacakson2.0的注解,并按照使用场景分类。
二. 常用注解
1. @JsonAutoDetect 自动检测
作用在类上,jackson拥有自动检测机制,允许使用任意的构造方法或工厂方法来构造实例,jackson默认的字段属性发现规则如下:
- 所有的 public 类型的属性
- 所有的 public 类型的 get 方法
- 所有的 set 方法(无论方法的可见性)
使用@JsonAutoDetect(作用在类上)来开启/禁止自动检测 ,配置注解属性**fieldVisibility**来调整字段的可见级别
- ANY:任何级别的字段都可以自动识别
- NONE:所有字段都不可以自动识别
- NON_PRIVATE:非private修饰的字段可以自动识别
- PROTECTED_AND_PUBLIC:被protected和public修饰的字段可以被自动识别
- PUBLIC_ONLY:只有被public修饰的字段才可以被自动识别
- DEFAULT:同PUBLIC_ONLY
配置getterVisibility=JsonAutoDetect.Visibility.NONE
举例:
@AllArgsConstructor
@NoArgsConstructor
//@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class TestPOJO{
private String name;
}
这个类我们只有一个private的name属性,并且没有提供对应的get,set方法,如果按照默认的属性发现规则我们将无法序列化和反序列化name字段(如果没有get,set方法,只有被public修饰的属性才会被发现),你可以通过修改@JsonAutoDetect的fieldVisibility来调整自动发现级别,为了使name被自动发现,我们需要将级别调整为ANY
除了fieldVisibility,还可以配置getterVisibility,setterVisibility,creatorVisibility等的可见级别。
2. @JsonIgnore 忽略序列化(HOT🔥)
作用在字段或方法上,用来完全忽略被注解的字段和方法对应的属性,即便这个字段或方法可以被自动检测到或者还有其他的注解,set和get方法任意添加一个即可,本例中的name和count都会被忽略解析
@JsonIgnore
public String name;
public int count;
@JsonIgnore
public void setCount(int count) {
this.count = count;
}
3. @JsonIgnoreProperties 批量忽略序列化(HOT🔥)
作用在类上,用来说明有些属性在序列化/反序列化时需要忽略掉,可以将它看做是@JsonIgnore的批量操作,但它的功能比@JsonIgnore要强,比如一个类是代理类,我们无法将将@JsonIgnore标记在属性或方法上,此时便可用@JsonIgnoreProperties标注在类声明上,它还有一个重要的功能是作用在反序列化时解析字段时过滤一些未知的属性,否则通常情况下解析到我们定义的类不认识的属性便会抛出异常。
可以注明是想要忽略的属性列表如@JsonIgnoreProperties({“name”,”age”,”title”}),
也可以注明过滤掉未知的属性如@JsonIgnoreProperties(ignoreUnknown=true)
4. @JsonProperty 序列化字段(HOT🔥)
作用在字段或方法上,用来对属性的序列化/反序列化,可以用来避免遗漏属性,同时提供对属性名称重命名
@JsonProperty
/* 注意这里必须得有该注解,因为没有提供对应的getId和setId函数,
* 而是其他的getter和setter,防止遗漏该属性 */
private int id;
@JsonProperty("第一个名字")
private String firstName;
5. @JsonUnwrapped 序列化细节
作用在字段或方法上,指定某个字段(类型是POJO)序列化成扁平化,而不是嵌套对象,在反序列化时再包装成对象。在2.0+版本中@JsonUnwrapped添加了**prefix和suffix**属性,用来对字段添加前后缀,这在有关属性分组上比较有用
举例:
public class TestPojo {
public int id;
@JsonUnwrapped(prefix = "pre_", suffix = "_suf")
public TestName testName;
}
private class TestName {
private String firstName;
private String secondName;
}
结果为:
{"id":111,"pre_firstName_suf":"张","pre_secondName_suf":"三"}
6. @JsonIdentityInfo 循环依赖
作用于类或属性上,2.0+版本新注解,,当entity被标注后,jackson在每一次序列化的时候都会为该实例生成专门的ID(也可以是实例自带的属性),通过这种方式辨别实例。这种方式适用于存在一个实体关联链的场景
,比如 Parent->Child->Perent。实际使用时应尽量避免这种循环依赖的情况,如果出现,可以使用@JsonIgnore忽略不解析的数据。
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")
public static class Parent{
private String name;
private Child[] children;
}
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public static class Child{
private String name;
private Parent parent;
}
7. @JsonNaming 批量命名
作用于类或方法上,jackson 2.1+版本的注解,注意这个注解是在jackson-databind包中而不是在jackson-annotations包里,它可以让你定制属性命名策略,作用和前面提到的@JsonProperty的重命名属性名称相同。
如果POJO里有很多属性,给每个属性都要加上@JsonProperty是多么繁重的工作,这里就需要用到@JsonNaming了,它不仅能制定统一的命名规则,还能任意按自己想要的方式定制。
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class TestPOJO{
public String id;
public String name;
}
命名策略
@JsonNaming使用了jackson已经实现的命名策PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy,它可以将大写转换为小写并添加下划线。你可以自定义,必须继承类PropertyNamingStrategy,建议继承PropertyNamingStrategyBase,我们自己实现一个类似LowerCaseWithUnderscoresStrategy的策略,只是将下划线改为破折号
public static class MyPropertyNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
@Override
public String translate(String input) {
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++)
{
char c = input.charAt(i);
if (i > 0 || c != '-') // skip first starting underscore
{
if (Character.isUpperCase(c))
{
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '-')
{
result.append('-');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
}
else
{
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
}
如果你想让自己定制的策略对所有解析都实现,除了对每个具体的实体类对应的位置加上@JsonNaming外你还可以如下做全局配置
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());
三. 多态类型处理注解
jackson允许配置多态类型处理,当进行反序列化时,JSON数据匹配的对象可能有多个子类型,为了正确的读取对象的类型,我们需要添加一些类型信息。可以通过下面几个注解来实现:
1. @JsonTypeInfo 多态解析
作用于类/接口,被用来开启多态类型处理,对基类/接口和子类/实现类都有效
这个注解有一些属性:
use**: 定义使用哪一种类型识别码**,它有下面几个可选值
JsonTypeInfo.Id.CLASS:使用完全限定类名做识别,如com.logicbig.example.TestPOJO
JsonTypeInfo.Id.**MINIMAL_CLASS**:若基类和子类在同一包类,使用类名(忽略包名)作为识别码
JsonTypeInfo.Id.**NAME:一个合乎逻辑的指定名称,结合@JsonSubTypes(name = “map”)**使用
JsonTypeInfo.Id.**C**USTOM:自定义识别码,由@JsonTypeIdResolver对应,稍后解释
JsonTypeInfo.Id.**NO**NE:不使用识别码
i**nclude(可选): 指定识别码是如何被包含进去的**,它有下面几个可选值:
JsonTypeInfo.As.**PROPERTY**:作为数据的兄弟属性
JsonTypeInfo.As.**EXISTING_PROPERTY**:作为POJO中已经存在的属性
JsonTypeInfo.As.**EXTERNAL_PROPERTY**:作为扩展属性
JsonTypeInfo.As.**WRAPPER_OBJECT**:作为一个包装的对象
JsonTypeInfo.As.**WRAPPER_ARRAY**:作为一个包装的数组
- property(可选)**:制定识别码的属性名称,仅当一下情况时有效**
- use
- JsonTypeInfo.Id.CLASS(若不指定property则默认为@class)
- use为JsonTypeInfo.Id.MINIMAL_CLASS(若不指定property则默认为@c)
- use为JsonTypeInfo.Id.NAME(若不指定property默认为@type),通常在这种情况下使用
- include
- JsonTypeInfo.As.PROPERTY
- JsonTypeInfo.As.EXISTING_PROPERTY
- JsonTypeInfo.As.EXTERNAL_PROPERTY
- use
- defaultImpl(可选)**:类型识别码不存在或者无效时指定反序列化时使用的默认类型**
- 通常在use为JsonTypeInfo.Id.NAME时,配合property**使用**
- visible(可选)**:是否可见,默认false**,通常不配置
- 属性定义了类型标识符的值是否会通过JSON流成为反序列化器的一部分,默认为fale,也就是说,jackson会从JSON内容中处理和删除类型标识符再传递给JsonDeserializer。
举例:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type",
defaultImpl = Son1.class
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Son1.class, name = "son1"),
@JsonSubTypes.Type(value = Son2.class, name = "son2"),
@JsonSubTypes.Type(value = Son3.class, name = "son3")
})
public Father {}
@JsonTypeInfo(use = Id.MINIMAL_CLASS)
@JsonSubTypes({
@JsonSubTypes.Type(Son1.class),
@JsonSubTypes.Type(Son2.class)
})
public Father {}
2. @JsonSubTypes 指定子类型
作用于类/接口,用来列出给定类的子类,只有当子类类型无法被检测到时才会使用它
一般是配合@JsonTypeInfo在基类上使用
@JsonSubTypes({
@JsonSubTypes.Type(Son1.class),
@JsonSubTypes.Type(Son2.class)
})
@JsonSubTypes的值是一个@JsonSubTypes.Type[]数组,里面枚举了多态类型(value对应类)和类型的标识符值(name对应@JsonTypeInfo中的property标识名称的值,此为可选值,若不制定需由@JsonTypeName在子类上制定)
3. @JsonTypeName 指定子类型的name
作用于子类,在未设置@JsonSubTypes 时用来为多态子类指定类型标识符的值,如果未在父类上设置@JsonSubTypes可以在子类上指定name
比如:
@JsonTypeName(value = "son1")
public Son1 {}
4. @JsonTypeResolver和@JsonTypeIdResoler(自定义多态)
作用于类,可以自定义多态的类型标识符,这个平时很少用到,主要是现有的一般就已经满足绝大多数的需求了,如果你需要比较特别的类型标识符,建议使用这2个注解,自己定制基于TypeResolverBuilder和TypeIdResolver的类即可
5. 例子
定义父类Super,子类Sub1和Sub2,创建TestPOJO内部的成员变量为Super数组,转换成json字符串。
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class JacksonUtilsTest {
public static void main(String[] args) throws Exception {
Sub1 sub1 = new Sub1();
sub1.setId(1);
sub1.setName("sub1Name");
Sub2 sub2 = new Sub2();
sub2.setId(2);
sub2.setAge(33);
ObjectMapper objectMapper = new ObjectMapper();
TestPOJO testPOJO = new TestPOJO();
testPOJO.setSupers(new Super[]{sub1, sub2});
String jsonStr = objectMapper.writeValueAsString(testPOJO);
//TestPOJO testPOJO_2 = objectMapper.readValue(jsonStr, TestPOJO.class);
System.out.println(jsonStr);
}
@Data
public static abstract class Super{
private int id;
}
@Data
public static class Sub1 extends Super{
private String name;
}
@Data
public static class Sub2 extends Super{
private int age;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class TestPOJO{
private JacksonUtilsTest.Super[] supers;
}
}
这是序列化时最简单的一种多态处理方式,因为没有使用任何多态处理注解,即默认使用的识别码类型为**JsonTypeInfo.Id.**NONE,而jackson没有自动搜索功能,所以只能序列化而不能反序列化(去掉注释会报错),上面序列化测试的结果格式化后为以下结果,我们可以看到JSON串中是没有对应的多态类型识别码的。
{
"supers": [
{
"id": 1,
"name": "sub1Name"
},
{
"id": 2,
"age": 33
}
]
}
下面我们在基类Super上加上多态处理相关注解,首先我们在基类Super上添加@JsonTy**peInfo(use = JsonTypeInfo.Id.CLASS)**即
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,property = "typeName")
public static abstract class Super{
private int id;
}
再次执行上面的序列化测试代码结果将会是:
{
"supers": [
{
"typeName": "com.logicbig.example.JacksonUtilsTest$Sub1",
"id": 1,
"name": "sub1Name"
},
{
"typeName": "com.logicbig.example.JacksonUtilsTest$Sub2",
"id": 2,
"age": 33
}
]
}
这次多态类型识别码的名称已经变成了我们指定的typeName而不是默认的@class了
上面的例子都是默认选择的include为**JsonTypeInfo.As.**PROPERTY,下面我们更改include方式,看看有什么变化,将include设置为**JsonTypeInfo.As.**WRAPPER_OBJECT即
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,include = JsonTypeInfo.As.WRAPPER_OBJECT,property = "typeName")
public static abstract class Super{
private int id;
}
结果为:
{
"supers": [
{
"com.logicbig.example.JacksonUtilsTest$Sub1": {
"id": 1,
"name": "sub1Name"
}
},
{
"com.logicbig.example.JacksonUtilsTest$Sub2": {
"id": 2,
"age": 33
}
}
]
}
我们看到类型识别码不再成为兄弟属性包含进去了而是为父属性将其他属性包含进去,此时我们指定的property=“typeName”已经无用了
再次修改use属性指定为JsonTypeInfo.Id.**MINIMAL_CLASS**,即
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,property = "typeName")
结果为:
{
"supers": [
{
"typeName": ".JacksonUtilsTest$Sub1",
"id": 1,
"name": "sub1Name"
},
{
"typeName": ".JacksonUtilsTest$Sub2",
"id": 2,
"age": 33
}
]
}
我们发现已经没有同包的package名称,识别码的值更加简短了
测试反序列化
此时我们取消之前反序列化的注释
TestPOJO testPOJO_2 = objectMapper.readValue(jsonStr, TestPOJO.class);
通过debug查看testPOJO_2的结构,我们可以看到已经反序列化成功了
可能我们在反序列化时觉得如此传递识别码很不友好,最好可以自定义识别码的值,可以选择use = JsonTypeInfo.Id.NAME和@JsonSubTypes配合即
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
@JsonSubTypes({@JsonSubTypes.Type(value=Sub1.class,name="sub1"),
@JsonSubTypes.Type(value=Sub2.class,name="sub2")})
public static abstract class Super{
private int id;
//getters、setters省略
}
结果为:
{
"supers": [
{
"@c": ".JacksonUtilsTest$Sub1",
"id": 1,
"name": "sub1Name"
},
{
"@c": ".JacksonUtilsTest$Sub2",
"id": 2,
"age": 33
}
]
}
也可以在子类上添加@JsonTypeName(value = “sub1”)和@JsonTypeName(value = “sub2”)以便取代父类上的@JsonSubTypes注解
如果想不使用@JsonSubTypes来侵入代码,我们可以在ObjectMapper上注册子类实现,即
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerSubtypes(new NamedType(Sub1.class,"sub1"));
objectMapper.registerSubtypes(new NamedType(Sub2.class,"sub2"));
四. 用于序列化和反序列化的注解
(未解析)
1. @JsonSerialize和@JsonDeserialize
作用于方法和字段上,通过 using(JsonSerializer)和using(JsonDeserializer)来指定序列化和反序列化的实现,通常我们在需要自定义序列化和反序列化时会用到,比如下面的例子中的日期转换
@Test
public void jsonSerializeAndDeSerialize() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
testPOJO.setBirthday(new Date());
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
System.out.println(jsonStr);
String jsonStr2 = "{\"name\":\"myName\",\"birthday\":\"2014-11-11 19:01:58\"}";
TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class);
System.out.println(testPOJO2.toString());
}
public static class TestPOJO{
private String name;
@JsonSerialize(using = MyDateSerializer.class)
@JsonDeserialize(using = MyDateDeserializer.class)
private Date birthday;
//getters、setters省略
@Override
public String toString() {
return "TestPOJO{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
private static class MyDateSerializer extends JsonSerializer<Date>{
@Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(value);
jgen.writeString(dateStr);
}
}
private static class MyDateDeserializer extends JsonDeserializer<Date>{
@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String value = jp.getValueAsString();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return dateFormat.parse(value);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
上面的例子中自定义了日期的序列化和反序列化方式,可以将Date和指定日期格式字符串之间相互转换。
也可以通过使用as(JsonSerializer)和as(JsonDeserializer)来实现多态类型转换,上面我们有提到多态类型处理时可以使用@JsonTypeInfo实现,还有一种比较简便的方式就是使用@JsonSerialize和@JsonDeserialize指定as的子类类型,注意这里必须指定为子类类型才可以实现替换运行时的类型
@Test
public void jsonSerializeAndDeSerialize() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
Sub1 sub1 = new Sub1();
sub1.setId(1);
sub1.setName("sub1Name");
Sub2 sub2 = new Sub2();
sub2.setId(2);
sub2.setAge(22);
testPOJO.setSub1(sub1);
testPOJO.setSub2(sub2);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
System.out.println(jsonStr);
String jsonStr2 = "{\"name\":\"myName\",\"sub1\":{\"id\":1,\"name\":\"sub1Name\"},\"sub2\":{\"id\":2,\"age\":22}}";
TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class);
System.out.println(testPOJO2.toString());
}
public static class TestPOJO{
private String name;
@JsonSerialize(as = Sub1.class)
@JsonDeserialize(as = Sub1.class)
private MyIn sub1;
@JsonSerialize(as = Sub2.class)
@JsonDeserialize(as = Sub2.class)
private MyIn sub2;
//getters、setters省略
@Override
public String toString() {
return "TestPOJO{" +
"name='" + name + '\'' +
", sub1=" + sub1 +
", sub2=" + sub2 +
'}';
}
}
public static class MyIn{
private int id;
//getters、setters省略
}
public static class Sub1 extends MyIn{
private String name;
//getters、setters省略
@Override
public String toString() {
return "Sub1{" +
"id=" + getId() +
"name='" + name + '\'' +
'}';
}
}
public static class Sub2 extends MyIn{
private int age;
//getters、setters省略
@Override
public String toString() {
return "Sub1{" +
"id=" + getId() +
"age='" + age +
'}';
}
}
上面例子中通过as来指定了需要替换实际运行时类型的子类,实际上上面例子中序列化时是可以不使用@JsonSerialize(as = Sub1.class)的,因为jackson可以自动将POJO转换为对应的JSON,而反序列化时由于无法自动检索匹配类型必须要指定@JsonDeserialize(as = Sub1.class)方可实现
最后@JsonSerialize可以配置include属性来指定序列化时被注解的属性被包含的方式,默认总是被包含进来,但是可以过滤掉空的属性或有默认值的属性,举个简单的过滤空属性的例子如下
@Test
public void jsonSerializeAndDeSerialize() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("");
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{}",jsonStr);
}
public static class TestPOJO{
@JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
private String name;
//getters、setters省略
}
2. @JsonPropertyOrder
作用在类上,被用来指明当序列化时需要对属性做排序,它有2个属性
一个是alphabetic:布尔类型,表示是否采用字母拼音顺序排序,默认是为false,即不排序
@Test
public void jsonPropertyOrder() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setA("1");
testPOJO.setB("2");
testPOJO.setC("3");
testPOJO.setD("4");
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"a\":\"1\",\"c\":\"3\",\"d\":\"4\",\"b\":\"2\"}",jsonStr);
}
public static class TestPOJO{
private String a;
private String c;
private String d;
private String b;
//getters、setters省略
}
我们先看一个默认的排序方式,序列化单元测试结果依次为{“a”:”1”,”c”:”3”,”d”:”4”,”b”:”2”},即是没有经过排序操作的,在TestPOJO上加上@jsonPropertyOrder(alphabetic = true)再执行测试结果将会为{“a”:”1”,”b”:”2”,”c”:”3”,”d”:”4”}
还有一个属性是value:数组类型,表示将优先其他属性排序的属性名称
@Test
public void jsonPropertyOrder() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setA("1");
testPOJO.setB("2");
testPOJO.setC("3");
testPOJO.setD("4");
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
System.out.println(jsonStr);
Assert.assertEquals("{"c":"3","b":"2","a":"1","d":"4"}",jsonStr);
}
@JsonPropertyOrder(alphabetic = true,value = {"c","b"})
public static class TestPOJO{
private String a;
private String c;
private String d;
private String b;
//getters、setters省略
}
上面例子可以看到value指定了c和b属性优先排序,所以序列化后为{“c”:”3”,”b”:”2”,”a”:”1”,”d”:”4”}
还记得本文上面最开始配置MapperFeature时也有属性排序么,对,就是
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);
只不过@JsonPropertyOrder颗粒度要更细一点,可以决定哪些属性优先排序
3. @JsonView
视图模板,作用于方法和属性上,用来指定哪些属性可以被包含在JSON视图中,在前面我们知道已经有@JsonIgnore和@JsonIgnoreProperties可以排除过滤掉不需要序列化的属性,可是如果一个POJO中有上百个属性,比如订单类、商品详情类这种属性超多,而我们可能只需要概要简单信息即序列化时只想输出其中几个或10几个属性,此时使用@JsonIgnore和@JsonIgnoreProperties就显得非常繁琐,而使用@JsonView便会非常方便,只许在你想要输出的属性(或对应的getter)上添加@JsonView即可,举例:
@Test
public void jsonView() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setA("1");
testPOJO.setB("2");
testPOJO.setC("3");
testPOJO.setD("4");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
String jsonStr = objectMapper.writerWithView(FilterView.OutputA.class).writeValueAsString(testPOJO);
Assert.assertEquals("{"a":"1","c":"3"}",jsonStr);
String jsonStr2 = objectMapper.writerWithView(FilterView.OutputB.class).writeValueAsString(testPOJO);
Assert.assertEquals("{"d":"4","b":"2"}",jsonStr2);
}
public static class TestPOJO{
@JsonView(FilterView.OutputA.class)
private String a;
@JsonView(FilterView.OutputA.class)
private String c;
@JsonView(FilterView.OutputB.class)
private String d;
@JsonView(FilterView.OutputB.class)
private String b;
//getters、setters忽略
}
private static class FilterView {
static class OutputA {}
static class OutputB {}
}
上面的测试用例中,我们在序列化之前先设置了objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false),看javadoc说这是一个双向开关,开启将输出没有JsonView注解的属性,false关闭将输出有JsonView注解的属性,可惜我在测试中开启开关后有JsonView注解的属性任然输出了,大家可以研究下。序列化时使用了objectMapper.writerWithView(FilterView.OutputA.class).writeValueAsString(testPOJO),即使用哪个视图来输出。在上面的例子中又2种视图,我们在序列化的时候可以选择想要的视图来输出,这在一些地方比较好用,比如安卓、苹果、桌面等不同的客户端可能会输出不同的属性。在1.6版本中这个@JsonView注解同时也会强制性自动发现,也就是说不管属性的可见性以及是否设置了自动发现这些属性都将会自动被发现,在上例中TestPOJO中的getters、setters可以不需要也能输出我们想要的结果。
4. @JsonFilter
Json属性过滤器,作用于类,作用同上面的@JsonView,都是过滤掉不想要的属性,输出自己想要的属性。和@FilterView不同的是@JsonFilter可以动态的过滤属性,比如我不想输出以system开头的所有属性等待,应该说@JsonFilter更高级一点,举个简单的例子
@Test
public void jsonFilter() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setA("1");
testPOJO.setB("2");
testPOJO.setC("3");
testPOJO.setD("4");
ObjectMapper objectMapper = new ObjectMapper();
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a"));
objectMapper.setFilters(filters);
String jsonStr = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"a\":\"1\"}",jsonStr);
}
@JsonFilter("myFilter")
public static class TestPOJO{
private String a;
private String c;
private String d;
private String b;
//getters、setters省略
}
上面例子中在我们想要序列化的POJO上加上了@JsonFilter,表示该类将使用名为myFilter的过滤器。在测试中定义了一个名为myFilter的SimpleFilterProvider,这个过滤器将会过滤掉所有除a属性以外的属性。这只是最简单的输出指定元素的例子,你可以自己实现FilterProvider来满足你的过滤需求。
有时候我们可能需要根据现有的POJO来过滤属性,而这种情况下通常不会让你修改已有的代码在POJO上加注解,这种情况下我们就可以结合@JsonFilter和MixInAnnotations来实现过滤属性,如下例所示,不再多做解释
@Test
public void jsonFilter() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setA("1");
testPOJO.setB("2");
testPOJO.setC("3");
testPOJO.setD("4");
ObjectMapper objectMapper = new ObjectMapper();
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",SimpleBeanPropertyFilter.filterOutAllExcept("a"));
objectMapper.setFilters(filters);
objectMapper.addMixInAnnotations(TestPOJO.class,MyFilterMixIn.class);
String jsonStr = objectMapper.writeValueAsString(testPOJO);
Assert.assertEquals("{\"a\":\"1\"}",jsonStr);
}
public static class TestPOJO{
private String a;
private String c;
private String d;
private String b;
//getters、setters省略
}
@JsonFilter("myFilter")
private static interface MyFilterMixIn{
}
5. @JsonIgnoreType
作用于类,表示被注解该类型的属性将不会被序列化和反序列化,也跟上面几个一样属于过滤属性功能的注解,举例:
@Test
public void jsonFilter() throws Exception {
TestPOJO testPOJO = new TestPOJO();
testPOJO.setName("myName");
Sub1 sub1 = new Sub1();
sub1.setId(1);
sub1.setName("sub1");
Sub2 sub2 = new Sub2();
sub2.setId(2);
sub2.setAge(22);
testPOJO.setMyIn(sub1);
testPOJO.setSub1(sub1);
testPOJO.setSub2(sub2);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(testPOJO);
System.out.println(jsonStr);
}
public static class TestPOJO{
private Sub1 sub1;
private Sub2 sub2;
private MyIn myIn;
private String name;
//getters、setters省略
}
public static class MyIn{
private int id;
//getters、setters省略
}
@JsonIgnoreType
public static class Sub1 extends MyIn{
private String name;
//getters、setters省略
}
@JsonIgnoreType
public static class Sub2 extends MyIn{
private int age;
//getters、setters省略
}
上面例子中我们在类Sub1和Sub2上都加上了@JsonIgnoreType,那么需要序列化和反序列时POJO中所有Sub1和Sub2类型的属性都将会被忽略,上面测试结果为{“myIn”:{“id”:1,”name”:”sub1”},”name”:”myName”},只输出了name和myIn属性。需要注意的是@JsonIgnoreType是可以继承的,即如果在基类上添加了该注解,那么子类也相当于加了该注解。在上例中,如果只在基类MyIn上添加@JsonIgnoreType那么序列化TestPOJO时将会过滤掉MyIn、Sub1、Sub2。输出结果为{“name”:”myName”}
6. @JsonAnySetter
作用于方法,在反序列化时用来处理遇到未知的属性的时候调用,在本文前面我们知道可以通过注解@JsonIgnoreProperties(ignoreUnknown=true)来过滤未知的属性,但是如果需要这些未知的属性该如何是好?那么@JsonAnySetter就可以派上用场了,它通常会和map属性配合使用用来保存未知的属性,举例:
@Test
public void jsonAnySetter() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{"name":"myName","code":"12345","age":12}";
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("myName",testPOJO.getName());
Assert.assertEquals("12345",testPOJO.getOther().get("code"));
Assert.assertEquals(12,testPOJO.getOther().get("age"));
}
public static class TestPOJO{
private String name;
private Map other = new HashMap();
@JsonAnySetter
public void set(String name,Object value) {
other.put(name,value);
}
//getters、setters省略
}
测试用例中我们在set方法上标注了@JsonAnySetter,每当遇到未知的属性时都会调用该方法
7. @JsonCreator
作用于方法,通常用来标注构造方法或静态工厂方法上,使用该方法来构建实例,默认的是使用无参的构造方法,通常是和@JsonProperty或@JacksonInject配合使用,举例
@Test
public void jsonCreator() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{\"full_name\":\"myName\",\"age\":12}";
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("myName",testPOJO.getName());
Assert.assertEquals(12, testPOJO.getAge());
}
public static class TestPOJO{
private String name;
private int age;
@JsonCreator
public TestPOJO(@JsonProperty("full_name") String name,@JsonProperty("age") int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
上面示例中是在构造方法上标注了@JsonCreator,同样你也可以标注在静态工厂方法上,比如:
@Test
public void jsonCreator() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{"name":"myName","birthday":1416299461556}";
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("myName",testPOJO.getName());
System.out.println(testPOJO.getBirthday());
}
public static class TestPOJO{
private String name;
private Date birthday;
private TestPOJO(String name,Date birthday){
this.name = name;
this.birthday = birthday;
}
@JsonCreator
public static TestPOJO getInstance(@JsonProperty("name") String name,@JsonProperty("birthday") long timestamp){
Date date = new Date(timestamp);
return new TestPOJO(name,date);
}
public String getName() {
return name;
}
public Date getBirthday() {
return birthday;
}
}
这个实例中,TestPOJO的构造方法是私有的,外面无法new出来该对象,只能通过工厂方法getInstance来构造实例,此时@JsonCreator就标注在工厂方法上。
除了这2种方式外,还有一种构造方式成为授权式构造器,也是我们平常比较常用到的,这个构造器只有一个参数,且不能使用@JsonProperty。举例:
@Test
public void jsonCreator() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{\"full_name\":\"myName\",\"age\":12}";
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("myName",testPOJO.getName());
Assert.assertEquals(12,testPOJO.getAge());
}
public static class TestPOJO{
private String name;
private int age;
@JsonCreator
public TestPOJO(Map map){
this.name = (String)map.get("full_name");
this.age = (Integer)map.get("age");
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
8. @JacksonInject
作用于属性、方法、构造参数上,被用来反序列化时标记已经被注入的属性,举例:
@Test
public void jacksonInject() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{"age":12}";
InjectableValues inject = new InjectableValues.Std().addValue("name","myName");
TestPOJO testPOJO = objectMapper.reader(TestPOJO.class).with(inject).readValue(jsonStr);
Assert.assertEquals("myName", testPOJO.getName());
Assert.assertEquals(12,testPOJO.getAge());
}
public static class TestPOJO{
@JacksonInject("name")
private String name;
private int age;
//getters、setters省略
}
上面例子中我们在反序列化前通过InjectableValues来进行注入我们想要的属性
9. @JsonPOJOBuilder
作用于类,用来标注如何定制构建对象,使用的是builder模式来构建,比如Value v = new ValueBuilder().withX(3).withY(4).build();这种就是builder模式来构建对象,通常会喝@JsonDeserialize.builder来配合使用,我们举个例子:
@Test
public void jacksonInject() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = "{\"name\":\"myName\",\"age\":12}";
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("myName", testPOJO.getName());
Assert.assertEquals(12,testPOJO.getAge());
}
@JsonDeserialize(builder=TestPOJOBuilder.class)
public static class TestPOJO{
private String name;
private int age;
public TestPOJO(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
@JsonPOJOBuilder(buildMethodName = "create",withPrefix = "with")
public static class TestPOJOBuilder{
private String name;
private int age;
public TestPOJOBuilder withName(String name) {
this.name = name;
return this;
}
public TestPOJOBuilder withAge(int age) {
this.age = age;
return this;
}
public TestPOJO create() {
return new TestPOJO(name,age);
}
}
在TestPOJOBuilder上有@JsonPOJOBuilder注解,表示所有的参数传递方法都是以with开头,最终构建好的对象是通过create方法来获得,而在TestPOJO上使用了@JsonDeserializer,告诉我们在反序列化的时候我们使用的是TestPOJOBuilder来构建此对象的
五. 过期注解
还有一些过期不推荐使用的注解,我们一笔带过,主要知道他们是跟哪些其他注解功能一样即可
- @JsonGetter
作用于方法,1.0版本开始的注解,已经过期,不推荐使用,改用@JsonProperty
- @JsonUseSerializer
作用于类和方法,1.5版本开始被移除了,改用@JsonSerialize
- @JsonSetter
作用于方法,1.0版本开始的注解,已过期,不推荐使用,改用@JsonProperty
- @JsonClass
作用于方法和类,1.9版本开始被移除了,改为@JsonDeserialize.as
- @JsonContentClass
作用于方法,1.9版本开始被移除了,改为@JsonDeserialize.contentAs
- @JsonKeyClass
作用于方法和类,1.9版本开始被移除了,改为@JsonDeserialize.keyAs
- @JsonUseDeserializer
作用于方法和类,1.5版本开始被移除了,改为@JsonDeserialize
**