Jackson注解示例:让您快速上手

概述

在本教程中,我们将深入探讨 Jackson Annotations。

学到什么

  • 如何使用现有注解
  • 如何创建自定义注解并且使用自定义注解

Jackson 序列化注解

@JsonAnyGetter

定义

允许灵活地将 Map 字段用作标准属性

例子

  • ExtendableBean 实体具有名称属性和一组键/值对形式的可扩展属性:
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"
}
  • 我们还可以使用可选参数 enabled=false 来禁用  @JsonAnyGetter().

@JsonGetter

定义

@JsonGetter 注解是@JsonProperty 注解的替代方法,标记在get方法上

例子

  • 在下面的示例中,我们将方法 getTheName() 指定为 MyBean 实体的名称属性的 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"));
}
  • 这个没啥好讲的

@JsonPropertyOrder

定义

我们可以使用  @JsonPropertyOrder 注解来控制属性在序列化时的顺序。

例子

@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}

输出

{
    "name":"My bean",
    "id":1
}

@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));
}

Jackson反序列化注解

@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);
}

@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);
}

@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);
    }
}
  • 这是我们不加JsonAnySetter注解的反序列化的 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"));
}

@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));
}

Jackson 属性包含注解

@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")));
}

@JsonIgnore

定义

用于标记要在字段级别忽略的属性。

例子

  • 使用  @JsonIgnore 来忽略序列化中的属性 id
public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}
  • 测试用例
@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")));
}

@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")));
}

@JsonInclude

定义

可以使用 @JsonInclude 来排除具有空/空/默认值的属性

例子

  • 从序列化中排除空值的例子:
@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")));
}

@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"));
}

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());
}

Jackson 通用注解

@JsonUnwrapped

定义

  • 定义了在序列化/反序列化时应该展开/展平的值。

例子

  • 让我们看看这是如何工作的;我们将使用注解来解包属性名称:
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"
}

自定义注解

定义

  • 如何创建自定义 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
}

禁用注解

定义

如何禁用所有 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
}

结论

在本文中,我们研究了 Jackson 注解,仅仅是挖掘了正确使用它们所能带来的灵活性表面。