1. 概述
本教程,我们将深入学习Jackson 各种注解的使用方法。
内容涉及注解的基本用法,如何创建自定义注解,以及如何禁用它们。
2. Jackson 序列化例子
本节,讲解关于序列化的注解。
2.1. @JsonAnyGetter
@JsonAnyGetter
注解主要用于动态字段,我们可以将一些不确定的字段维护在一个Map
对象中,并像标准属性一样序列化,非常灵活。
例如,ExtendableBean
实体类有一个name
属性,还有一些扩展属性存放在properties
这个map中:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
}
当我们序列化后,map中的扩展属性不会挂在properties
节点下,而是直接映射到JSON根节点下:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
下面是实际测试代码:
@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("attr1"));
assertThat(result, containsString("val1"));
}
@JsonAnyGetter()
有个enable
参数,设为false
时,可以禁用此功能。此时序列化后map
中的属性会挂在properties
节点下。
反序列化时与之对应的有一个 @JsonAnySetter 注解,在后面的章节中会提供。
2.2. @JsonGetter
@JsonGetter
注解可以替代@JsonProperty
注解,将一个方法标记为getter方法。
下面例子中,我们指定了getTheName()
作为MyBean
实体类name
属性的getter
方法:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
下面是使用示例代码:
@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
2.3. @JsonPropertyOrder
使用@JsonPropertyOrder
注解可自定义属性序列化顺序。
例如,指定MyBean
属性序列化顺序:
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
下面是序列化后的输出:
{
"name":"My bean",
"id":1
}
测试:
@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
我们还可以使用@JsonPropertyOrder(alphabetic=true)
,按字母表顺序对属性进行排序。此时,序列化后的结果变为:
{
"id":1,
"name":"My bean"
}
2.4. @JsonRawValue
@JsonRawValue
注解可以要求Jackson按原样序列化属性。
例如下面实体类的json
字段是一个JSON字符串。我们想让其作为内嵌JSON原样输出,而不是一个字符串,则可以使用@JsonRawValue
注解:
public class RawBean {
public String name;
@JsonRawValue
public String json;
}
序列化后的结果为:
{
"name":"My bean",
"json":{
"attr":false
}
}
测试:
@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
throws JsonProcessingException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("{\"attr\":false}"));
}
2.5. @JsonValue
被@JsonValue
修饰的字段(或getter方法)为该类序列化后的结果。
例如,在一个枚举中,我们用@JsonValue
注解了getName
,那么整个序列化结果就是name
的值:
public enum TypeEnumWithValue {
TYPE1(1, "Type A"), TYPE2(2, "Type 2");
private Integer id;
private String name;
// standard constructors
@JsonValue
public String getName() {
return name;
}
}
测试:
@Test
public void whenSerializingUsingJsonValue_thenCorrect()
throws JsonParseException, IOException {
String enumAsString = new ObjectMapper()
.writeValueAsString(TypeEnumWithValue.TYPE1);
assertThat(enumAsString, is(""Type A""));
}
2.6. @JsonRootName
@JsonRootName
注解用来指定root wrapper的名字。注意,只有当WRAP_ROOT_VALUE
开启时,此注解才生效。
比如,我们不想把User
序列化下面这样:
{
"id": 1,
"name": "John"
}
而是想添加一个root wrapper,挂载在"User"节点下:
{
"User": {
"id": 1,
"name": "John"
}
}
我们将用@JsonRootName
注解指明root wrapper的名字:
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
默认情况下,如果不指定value
,则默认使用类名 —— 这里把UserWithRoot
作为root wrapper
的名字。
@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new User(1, "John");
ObjectMapper mapper = new ObjectMapper();
// 只有当`WRAP_ROOT_VALUE`开启时,此注解才生效。
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = mapper.writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, containsString("user"));
}
输出:
{
"user":{
"id":1,
"name":"John"
}
}
自Jackson 2.4,新增了一个可选参数namespace
可用于XML等数据格式。如果添加它,它将成为完全限定名称的一部分:
@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
public int id;
public String name;
// ...
}
如果使用XmlMapper
将其序列化为XML,输出结果为:
<user xmlns="users">
<id xmlns="">1</id>
<name xmlns="">John</name>
<items xmlns=""/>
</user>
2.7. @JsonSerialize
使用@JsonSerialize注解,自定义序列化器。
来看个例子,我们用@JsonSerialize
指定通过CustomDateSerializer
来序列化eventDate
属性。
public class EventWithSerializer {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
下面是自定义序列化CustomDateSerializer的定义:
public class CustomDateSerializer extends StdSerializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(
Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
测试:
@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithSerializer event = new EventWithSerializer("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
3. Jackson 反序列化注解
接下来,让我们继续探索Jackson关于反序列化的注解。
3.1. @JsonCreator
@JsonCreator注解指定对象反序列化时,使用的构造函数或者工厂方法。
当我们需要解析的JSON和目标实体类不匹配,需要做一些特殊操作时,这个注解就很有用。另外,Jackson默认会调用对象的无参构造函数,但是如果我们定义了有参构造函数,但没有提供无参构造函数时,Jackson就会报错,此时也可以用到@JsonCreator
来解决这个问题。
假设我们要反序列化下面的JSON:
{
"id":1,
"theName":"My bean"
}
但是,我们的目标实体类中没有 theName
这个字段,只有一个 name 字段。但我们不想修改实体类本身,我们只需要通过使用@JsonCreator
指定构造函数并结合 @JsonProperty
注解来控制反序列化流程:
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;
}
}
测试:
@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"theName\":\"My bean\"}";
BeanWithCreator bean = new ObjectMapper()
.readerFor(BeanWithCreator.class)
.readValue(json);
assertEquals("My bean", bean.name);
}
3.2. @JacksonInject
使用 @JacksonInject 注解后,属性的值将从我们注入的值中获取,而不是从JSON中。
下面我们演示使用 @JacksonInject 注入 id字段:
public class BeanWithInject {
@JacksonInject
public int id;
public String name;
}
实现:
@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
throws IOException {
String json = "{\"name\":\"My bean\"}";
InjectableValues inject = new InjectableValues.Std()
.addValue(int.class, 1);
BeanWithInject bean = new ObjectMapper().reader(inject)
.forType(BeanWithInject.class)
.readValue(json);
assertEquals("My bean", bean.name);
// 这里bean.id的值来自inject
assertEquals(1, bean.id);
}
3.3. @JsonAnySetter
使用 @JsonAnySetter 时我们可以利用 Map 灵活性,序列化的时候将所有JSON字段都添加到map中。
需要反序列化的 ExtendableBean 实体定义如下:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}
需要反序列化的JSON数据:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
测试代码:
@Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
throws IOException {
String json
= "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
ExtendableBean bean = new ObjectMapper()
.readerFor(ExtendableBean.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals("val2", bean.getProperties().get("attr2"));
}
3.4.@JsonSetter
@JsonSetter 是 @JsonProperty 的替代品,它将方法标记为 setter 方法。
当我们需要读取的JSON 数据和目标entity字段名称不一样时,这个注解就派上用场了。
下面例子中,我们指定 setTheName()方法作为name字段的setter方法
public class MyBean {
public int id;
private String name;
@JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
测试代码:
@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"name\":\"My bean\"}";
MyBean bean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(json);
assertEquals("My bean", bean.getTheName());
}
3.5. @JsonDeserialize
@JsonDeserialize 用于指定使用自定义的反序列化器。
下面例子我们指定 eventDate 字段使用 CustomDateDeserializer来实现反序列化:
public class EventWithSerializer {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
CustomDateDeserializer 定义如下:
public class CustomDateDeserializer
extends StdDeserializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Date deserialize(
JsonParser jsonparser, DeserializationContext context)
throws IOException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
测试代码:
@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
throws IOException {
String json
= "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
EventWithSerializer event = new ObjectMapper()
.readerFor(EventWithSerializer.class)
.readValue(json);
assertEquals(
"20-12-2014 02:30:00", df.format(event.eventDate));
}
3.6. @JsonAlias
@JsonAlias 为字段在反序化时定义一个或多个别名。
public class AliasBean {
@JsonAlias({ "fName", "f_name" })
private String firstName;
private String lastName;
}
本例中JSON 中的含有 “fName”、“f_name” 或 firstName 的字段都可以被反序列化到 POJO的firstName属性中。
测试代码:
@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
assertEquals("John", aliasBean.getFirstName());
}
4. Jackson 包含/排除某个属性
4.1. @JsonIgnoreProperties
@JsonIgnoreProperties 是一个 class 级的注解,用于标记Jackson需要忽略的属性列表。
让我们看看如何忽略 "id" 字段:
@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
public int id;
public String name;
}
现在测试看看id字段序列化时是否被忽略:
@Test
public void whenSerializingUsingJsonIgnoreProperties_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
要忽略 JSON 输入的未知字段而不抛异常,可设置 @JsonIgnoreProperties 注解中的ignoreUnknown=true
4.2. @JsonIgnore
也可使用 @JsonIgnore 来忽略某个属性,这是一个字段级的注解。
public class BeanWithIgnore {
@JsonIgnore
public int id;
public String name;
}
测试id字段是否被忽略:
@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
4.3. @JsonIgnoreType
@JsonIgnoreType 用于标记忽略某个类型的属性。
如下,我们将忽略所有Name类型的属性:
public class User {
public int id;
public Name name;
@JsonIgnoreType
public static class Name {
public String firstName;
public String lastName;
}
}
测试:
@Test
public void whenSerializingUsingJsonIgnoreType_thenCorrect()
throws JsonProcessingException, ParseException {
User.Name name = new User.Name("John", "Doe");
User user = new User(1, name);
String result = new ObjectMapper()
.writeValueAsString(user);
assertThat(result, containsString("1"));
// firstName和lastName将被忽略
assertThat(result, not(containsString("name")));
assertThat(result, not(containsString("John")));
}
4.4. @JsonInclude
使用 @JsonInclude 可用于排除属性值为 空/null的字段。
如下,我们定义只序列化非NULL的字段:
@JsonInclude(Include.NON_NULL)
public class MyBean {
public int id;
public String name;
}
测试代码:
public void whenSerializingUsingJsonInclude_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, null);
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
}
4.5 @JsonIncludeProperties
@JsonIncludeProperties 是Jackson呼声最多的注解之一,于Jackson 2.12版本中引入。 用于标记序列化/反序列化过程中需要包含的属性列表。
例如下面序列化时,输出的结果只包含 name
字段:
@JsonIncludeProperties({ "name" })
public class BeanWithInclude {
public int id;
public String name;
}
测试验证
@Test
public void whenSerializingUsingJsonIncludeProperties_thenCorrect() throws JsonProcessingException {
final BeanWithInclude bean = new BeanWithInclude(1, "My bean");
final String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
assertThat(result, containsString("name"));
}
4.6. @JsonAutoDetect
默认情况下,Jackson 只对具有 public 的字段或带有getters/setters的字段进行序列化和反序列化。使用 @JsonAutoDetect 可以覆盖这种行为,指定字段、方法的可见性规则。
下面例子,我们允许对private属性进行序列化:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
private int id;
private String name;
}
测试:
@Test
public void whenSerializingUsingJsonAutoDetect_thenCorrect()
throws JsonProcessingException {
PrivateBean bean = new PrivateBean(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("My bean"));
}
5. Jackson 多态类型处理
下面我们学习 Jackson 对多态类型处理的支持:
- @JsonTypeInfo – 指示序列化中包含的类型信息的详细信息
- @JsonSubTypes – 用于感知被注解类有那些子类
- @JsonTypeName – 用于指定被注解类的逻辑名称
下面我们来看一个复杂的示例,序列化/反序列化Zoo实体类,用到@JsonTypeInfo、@JsonSubTypes以及@JsonTypeName 这三个注解。
public class Zoo {
public Animal animal;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public static class Animal {
public String name;
}
@JsonTypeName("dog")
public static class Dog extends Animal {
public double barkVolume;
}
@JsonTypeName("cat")
public static class Cat extends Animal {
boolean likesCream;
public int lives;
}
}
序列化测试:
@Test
public void whenSerializingPolymorphic_thenCorrect()
throws JsonProcessingException {
Zoo.Dog dog = new Zoo.Dog("lacy");
Zoo zoo = new Zoo(dog);
String result = new ObjectMapper()
.writeValueAsString(zoo);
assertThat(result, containsString("type"));
assertThat(result, containsString("dog"));
}
Zoo(实际类型为Dog)序列化结果:
{
"animal": {
"type": "dog",
"name": "lacy",
"barkVolume": 0
}
}
下面演示反序列化,JSON输入如下:
{
"animal":{
"name":"lacy",
"type":"cat"
}
}
测试代码:
@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws IOException {
String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";
Zoo zoo = new ObjectMapper()
.readerFor(Zoo.class)
.readValue(json);
assertEquals("lacy", zoo.animal.name);
// 反序列化后对象类型应该是Zoo的子类: Cat
assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}
6. Jackson 其他通用注解
6.1. @JsonProperty
@JsonProperty 注解用来将JSON字符串中的属性名和Java类中的属性名对应起来。
例如下面 name
字段的getter和setter方法名和JSON中的不一样,此时就可以用到 @JsonProperty 注解。
public class MyBean {
public int id;
private String name;
@JsonProperty("name")
public void setTheName(String name) {
this.name = name;
}
@JsonProperty("name")
public String getTheName() {
return name;
}
}
验证测试:
@Test
public void whenUsingJsonProperty_thenCorrect()
throws IOException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
MyBean resultBean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(result);
assertEquals("My bean", resultBean.getTheName());
}
6.2. @JsonFormat 注解
** @JsonFormat 注解用于指定序列化Date/Time类型字段的格式。**
在下面的示例中,我们使用 @JsonFormat 来控制 eventDate 字段的格式:
public class EventWithFormat {
public String name;
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}
测试代码:
@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithFormat event = new EventWithFormat("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
6.3. @JsonUnwrapped
@JsonUnwrapped 注解用来偏平化、展开嵌套字段。
例如我们将 name
字段进行进行 unwrapped 操作:
public class UnwrappedUser {
public int id;
@JsonUnwrapped
public Name name;
public static class Name {
public String firstName;
public String lastName;
}
}
下面我们来试试序列化后是什么样子
@Test
public void whenSerializingUsingJsonUnwrapped_thenCorrect()
throws JsonProcessingException, ParseException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, not(containsString("name")));
}
我们可以看到输出的结果,没有name
字段,其下包裹的字段被提到了外层
{
"id":1,
"firstName":"John",
"lastName":"Doe"
}
6.4. @JsonView 注解
@JsonView 注解可用于动态控制需要包含的序列化/反序列字段
下面我们将使用 @JsonView 注解来序列化 Item
实体。
首先定义 View:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
然后定义 Item
实体,并添加 view 注解:
public class Item {
@JsonView(Views.Public.class)
public int id;
@JsonView(Views.Public.class)
public String itemName;
@JsonView(Views.Internal.class)
public String ownerName;
}
然后测试:
@Test
public void whenSerializingUsingJsonView_thenCorrect()
throws JsonProcessingException {
Item item = new Item(2, "book", "John");
String result = new ObjectMapper()
.writerWithView(Views.Public.class)
.writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("2"));
// 结果将不包含 ownerName 字段
assertThat(result, not(containsString("John")));
}
6.5. @JsonManagedReference注解, @JsonBackReference注解
@JsonManagedReference 和 @JsonBackReference 注释可处理parent/child依赖关系,解决循环引用的问题。
下面例子中,UserWithRef
和 ItemWithRef
存在相互引用 。如果不加注解,会抛 StackOverflowError 的异常。
ItemWithRef 定义:
public class ItemWithRef {
public int id;
public String itemName;
@JsonManagedReference
public UserWithRef owner;
}
UserWithRef 定义:
public class UserWithRef {
public int id;
public String name;
@JsonBackReference
public List<ItemWithRef> userItems;
}
测试代码:
@Test
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect()
throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, not(containsString("userItems")));
}
6.6. @JsonIdentityInfo 注解
@JsonIdentityInfo 主要作用是将对象之间的引用表示为唯一的标识符,而不是完整的对象,通常用于解决循环引用问题。
下面例子中,ItemWithIdentity
实体和 UserWithIdentity
之间有双向引用关系:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}
UserWithIdentity 实体:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class UserWithIdentity {
public int id;
public String name;
public List<ItemWithIdentity> userItems;
}
下面就不会出现循环引用的问题了:
@Test
public void whenSerializingUsingJsonIdentityInfo_thenCorrect()
throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, containsString("userItems"));
}
下面是完整的序列化结果:
{
"id": 2,
"itemName": "book",
"owner": {
"id": 1,
"name": "John",
"userItems": [
2
]
}
}
6.7. @JsonFilter 注解
@JsonFilter 注解指定序列化时要使用的过滤器。
首先,@JsonFilter 指定过滤器的名称:
@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
下面是完成测试代码,我们定义一个名为myFilter的过滤器,序列化时将排除除 name
之外的其他属性:
@Test
public void whenSerializingUsingJsonFilter_thenCorrect()
throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters
= new SimpleFilterProvider().addFilter(
"myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper()
.writer(filters)
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
7. 自定义 Jackson 注解
可以利用 @JacksonAnnotationsInside 创建自定义Jackson注解:
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id", "dateCreated" })
public @interface CustomAnnotation {}
我们创建了名为CustomAnnotation的注解,现在应用到我们的实体类上试试:
@CustomAnnotation
public class BeanWithCustomAnnotation {
public int id;
public String name;
public Date dateCreated;
}
可以看到我们将多个现有的Jackson组合到一个注解上了,更简洁:
@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
throws JsonProcessingException {
BeanWithCustomAnnotation bean
= new BeanWithCustomAnnotation(1, "My bean", null);
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
assertThat(result, not(containsString("dateCreated")));
}
序列化输出结果:
{
"name":"My bean",
"id":1
}
8. Mixin - 混合注解
Mixin 即混合的意思。使用Jackson MixIn 注解功能,可以在不修改实际类的基础上添加注解,例如一些第三方库我们没法修改源码为其添加注解。
例如,下面的例子。通过我们希望忽略Item中User类型的字段:
public class Item {
public int id;
public String itemName;
public User owner;
}
直接的做法是,修改User类定义,添加 @JsonIgnoreType
注解
@JsonIgnoreType
class User {
...
}
但假设,现在没法直接修改User类,那就可以通过Mixin类实现:
@JsonIgnoreType
public class MyMixInForIgnoreType {}
使用:
@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
throws JsonProcessingException {
Item item = new Item(1, "book", null);
// 混合前,包含onwer
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("owner"));
// 混合后,不包含onwer
ObjectMapper mapper = new ObjectMapper();
// 关键方法
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);
result = mapper.writeValueAsString(item);
assertThat(result, not(containsString("owner")));
}
上面,通过ObjectMapper的addMixIn方法,添加我们要混入的类。addMixIn的定义:
/**
* @param target - 被混入的目标类
* @param mixinSource - 要混入注解的来源类
*
/
ObjectMapper addMixIn(Class<?> target, Class<?> mixinSource)
9. 禁用 Jackson 注解
如何禁用所有Jackson注解?我们可以通过设置 MapperFeature.USE_ANNOTATIONS
参数实现。例如我们下面的实体用到注解:
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
禁用前输出会结果:
{"id":1}
下面禁用注解:
@Test
public void whenDisablingAllAnnotations_thenAllDisabled()
throws IOException {
MyBean bean = new MyBean(1, null);
ObjectMapper mapper = new ObjectMapper();
// 禁用注解
mapper.disable(MapperFeature.USE_ANNOTATIONS);
String result = mapper.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("name"));
}
禁用后结果:
{
"id":1,
"name":null
}
10. 总结
在本文中,我们研究了Jackson注释,只是简单介绍了正确使用它们可以获得的灵活性。
本文中的所有示例代码,可从GitHub上获取。