Jackson 注解示例
最后更新:2023年4月14日
1. 概述
在本教程中,我们将深入研究 Jackson 注解。
我们将学习如何使用现有的注解,如何创建自定义注解,以及最后如何禁用它们。
更多阅读
2. Jackson 序列化注解
首先,我们将看一下序列化注解。
2.1. @JsonAnyGetter
@JsonAnyGetter 注解允许使用 Map 字段作为标准属性,从而提供灵活性。
例如,ExtendableBean 实体具有 name 属性和一组以键/值对形式存在的可扩展属性
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
}
当我们序列化此实体的实例时,我们将获得 Map 中的所有键值对作为标准的、简单的属性
{
"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"));
}
我们还可以使用可选参数 enabled 作为 false 来禁用 @JsonAnyGetter()。在这种情况下,Map 将被转换为 JSON,并在序列化后显示在 properties 变量下。
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 将属性完全按照原样序列化。
在下面的示例中,我们使用 @JsonRawValue 将一些自定义 JSON 作为实体的值嵌入
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}"));
}
我们还可以使用可选的布尔参数 value,该参数定义此注解是否处于活动状态。
2.5. @JsonValue
@JsonValue 指示库将用于序列化整个实例的单个方法。
例如,在枚举中,我们使用 @JsonValue 注解 getName,以便任何此类实体都通过其名称进行序列化
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 注解来指定要使用的根包装器的名称。
包装意味着,而不是将 User 序列化为如下内容
{
"id": 1,
"name": "John"
}
它将被这样包装
{
"User": {
"id": 1,
"name": "John"
}
}
所以让我们看一个例子。我们将会使用 @JsonRootName 注解来指示这个潜在的包装实体名称
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
默认情况下,包装器的名称将是类的名称 – UserWithRoot。通过使用该注解,我们可以得到更简洁的user:
@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new User(1, "John");
ObjectMapper mapper = new ObjectMapper();
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 进行序列化,输出将会是
<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;
}
这是简单的自定义 Jackson 序列化器
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 时,它非常有用。
让我们看一个例子。假设我们需要反序列化以下 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);
assertEquals(1, bean.id);
}
3.3. @JsonAnySetter
@JsonAnySetter 允许我们使用 Map 作为标准属性,从而获得灵活性。在反序列化时,JSON 中的属性将简单地添加到 map 中。
首先,我们将使用 @JsonAnySetter 来反序列化实体 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 数据时,这非常有用,但是 目标实体类与该数据不完全匹配,因此我们需要调整过程使其适应。
在下面的示例中,我们将指定方法 setTheName() 作为我们的 MyBean 实体中 name 属性的 setter
public class MyBean {
public int id;
private String name;
@JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
现在,当我们需要反序列化一些 JSON 数据时,这可以完美地工作
@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 指示使用自定义反序列化器。
首先,我们将使用 @JsonDeserialize 使用 CustomDateDeserializer 来反序列化 eventDate 属性
public class EventWithSerializer {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
这是自定义反序列化器
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;
}
我们有一个 POJO,并且想将类似 fName、f_name 和 firstName 的 JSON 值反序列化到 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 是一个类级注解,它标记 Jackson 将忽略的属性或属性列表。
让我们看一个快速示例,在序列化时忽略属性 id
@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
public int id;
public String name;
}
现在这是测试,确保忽略生效
@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 注解用于标记在字段级别要忽略的属性。
让我们使用 @JsonIgnore 忽略序列化中的属性 id
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"));
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 中引入的,可用于标记 Jackson 在序列化和反序列化期间将包含的属性或属性列表。
让我们看一个在序列化时包含属性 name 的快速示例
@JsonIncludeProperties({ "name" })
public class BeanWithInclude {
public int id;
public String name;
}
现在这是测试,确保我们只包含 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
@JsonAutoDetect 可以覆盖默认语义,即 哪些属性可见以及哪些不可见。
首先,让我们通过一个简单的示例看看该注解如何非常有用;让我们启用序列化私有属性
@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 – 定义用于已注解类的逻辑类型名称
让我们检查一个更复杂的示例,并使用全部三个 – @JsonTypeInfo、@JsonSubTypes, 和 @JsonTypeName – 来序列化/反序列化实体 Zoo
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"));
}
序列化带有 Dog 的 Zoo 实例的结果如下
{
"animal": {
"type": "dog",
"name": "lacy",
"barkVolume": 0
}
}
现在进行反序列化。让我们从以下 JSON 输入开始
{
"animal":{
"name":"lacy",
"type":"cat"
}
}
然后让我们看看这如何被反序列化成一个 Zoo 实例
@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);
assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}
6. Jackson 通用注解
接下来让我们讨论 Jackson 的一些更通用的注解。
6.1. @JsonProperty
我们可以添加 @JsonProperty 注解来指示 JSON 中的属性名称。
让我们使用 @JsonProperty 来序列化/反序列化属性 name,当我们处理非标准的 getter 和 setter 时
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 注解指定在序列化日期/时间值时使用的格式。
在下面的示例中,我们使用 @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
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")));
}
最后,这是输出的样子 – 静态嵌套类的字段与另一个字段一起展开
{
"id":1,
"firstName":"John",
"lastName":"Doe"
}
6.4. @JsonView
@JsonView 指示属性将包含在序列化/反序列化中的视图。
例如,我们将使用 @JsonView 来序列化 Item 实体的一个实例。
首先,让我们从视图开始
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
接下来,这是使用视图的 Item 实体
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"));
assertThat(result, not(containsString("John")));
}
6.5. @JsonManagedReference, @JsonBackReference
@JsonManagedReference 和 @JsonBackReference 注解可以处理父/子关系 并解决循环引用问题。
在下面的示例中,我们使用 @JsonManagedReference 和 @JsonBackReference 来序列化我们的 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("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
现在在完整的测试中,我们定义过滤器,它排除序列化过程中的所有其他属性,只保留 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 注解
接下来,让我们看看如何创建自定义 Jackson 注解。 我们可以利用 @JacksonAnnotationsInside 注解:
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id", "dateCreated" })
public @interface CustomAnnotation {}
现在,如果我们对实体使用新的注解
@CustomAnnotation
public class BeanWithCustomAnnotation {
public int id;
public String name;
public Date dateCreated;
}
我们可以看到它将现有的注解组合成一个简单的自定义注解,我们可以将其用作简写
@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. Jackson MixIn 注解
接下来,让我们看看如何使用 Jackson MixIn 注解。
例如,让我们使用 MixIn 注解来忽略 User 类型的属性
public class Item {
public int id;
public String itemName;
public User owner;
}
@JsonIgnoreType
public class MyMixInForIgnoreType {}
然后让我们在实际操作中看看这个效果
@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
throws JsonProcessingException {
Item item = new Item(1, "book", null);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("owner"));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);
result = mapper.writeValueAsString(item);
assertThat(result, not(containsString("owner")));
}
9. 禁用 Jackson 注解
最后,让我们看看如何 禁用所有 Jackson 注解。 我们可以通过禁用 MapperFeature.USE_ANNOTATIONS 来实现,如下例所示
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
现在,在禁用注解之后,它们应该不再起作用,并且库的默认设置应该适用
@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}
禁用注解之后的序列化结果
{
"id":1,
"name":null
}
10. 结论
在本文中,我们研究了 Jackson 注解,只是触及了通过正确使用它们可以获得的灵活性的表面。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















