首页 > 其他分享 >Jackson Annotations(注解)详解

Jackson Annotations(注解)详解

时间:2023-12-23 15:44:22浏览次数:39  
标签:Jackson String public bean 详解 result new Annotations name

转载自:https://blog.csdn.net/wjw465150/article/details/127326849

1. 概述

在本教程中,我们将深入研究 Jackson Annotations

我们将了解如何使用现有的注解,如何创建自定义注解,最后,如何禁用它们。

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

The output of serializing the entity is:

{
    "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""));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.6. @JsonRootName

如果启用了包装,则使用 @JsonRootName 注解来指定要使用的根包装器的名称。

包装意味着不是将 User 序列化为:

{
    "id": 1,
    "name": "John"
}
 
  • 1
  • 2
  • 3
  • 4

它将像这样包装:

{
    "User": {
        "id": 1,
        "name": "John"
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以让我们看一个例子。 我们将使用 @JsonRootName 注解来指示这个潜在包装实体的名称:

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

默认情况下,包装器的名称将是类的名称 - 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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这是序列化的输出:

{
    "user":{
        "id":1,
        "name":"John"
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

从 Jackson 2.4 开始,一个新的可选参数 namespace 可用于 XML 等数据格式。 如果我们添加它,它将成为完全限定名称的一部分:

@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
    public int id;
    public String name;

    // ...
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果我们用 XmlMapper 序列化它,输出将是:

<user xmlns="users">
    <id xmlns="">1</id>
    <name xmlns="">John</name>
    <items xmlns=""/>
</user>Copy
 
  • 1
  • 2
  • 3
  • 4
  • 5

2.7. @JsonSerialize

@JsonSerialize 表示编组实体时要使用的自定义序列化程序。

让我们看一个简单的例子。 我们将使用 @JsonSerialize 通过 CustomDateSerializer 序列化 eventDate 属性:

public class EventWithSerializer {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这是简单的自定义 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));
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

现在让我们在测试中使用它们:

@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));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3. Jackson 反序列化注解

接下来让我们研究Jackson反序列化注解。

3.1. @JsonCreator

我们可以使用 @JsonCreator 注解来调整反序列化中使用的构造函数/工厂。

当我们需要反序列化一些与我们需要获取的目标实体不完全匹配的 JSON 时,它非常有用。

让我们看一个例子。 假设我们需要反序列化以下 JSON:

{
    "id":1,
    "theName":"My bean"
}
 
  • 1
  • 2
  • 3
  • 4

但是,我们的目标实体中没有 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;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

让我们看看它的实际效果:

@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);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.2. @JacksonInject

@JacksonInject 表示属性将从注入而不是从 JSON 数据中获取其值。

在以下示例中,我们使用 @JacksonInject 来注入属性 id

public class BeanWithInject {
    @JacksonInject
    public int id;
    
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以下是它的工作原理:

@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);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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);
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这是我们需要反序列化的 JSON:

{
    "name":"My bean",
    "attr2":"val2",
    "attr1":"val1"
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

下面是它们是如何联系在一起的:

@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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.4. @JsonSetter

@JsonSetter是*@JsonProperty*的替代方法,它将该方法标记为setter方法。

当我们需要读取一些JSON数据,但目标实体类并不完全匹配该数据时,这是非常有用的,因此我们需要调优流程以使其适合。

在下面的示例中,我们将指定方法 setTheName() 作为 MyBean 实体中 name 属性的设置器:

public class MyBean {
    public int id;
    private String name;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

现在,当我们需要解组一些 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());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.5. @JsonDeserialize

@JsonDeserialize 表示使用自定义反序列化器。

首先,我们将使用 @JsonDeserialize 通过 CustomDateDeserializer 反序列化 eventDate 属性:

public class EventWithSerializer {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这是自定义反序列化器:

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);
        }
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

接下来是一个背靠背测试:

@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));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.6. @JsonAlias

@JsonAlias 在反序列化期间为属性定义一个或多个可选名称。

让我们通过一个简单的例子来看看这个注解是如何工作的:

public class AliasBean {
    @JsonAlias({ "fName", "f_name" })
    private String firstName;   
    private String lastName;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

这里我们有一个POJO,我们希望将带有 fNamef_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());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4. Jackson 针对属性的其它注解

4.1. @JsonIgnoreProperties

@JsonIgnoreProperties 是一个类级别的注解,它标记了Jackson将忽略的一个属性或一组属性。

让我们看一个简单的例子,序列化时忽略属性 id:

@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
    public int id;
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

下面的测试确保忽略发生:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

为了毫无例外地忽略JSON输入中的任何未知属性,我们可以设置 @JsonIgnoreProperties 注解的 ignoreUnknown=true

4.2. @JsonIgnore

相比之下,@JsonIgnore 注解用于在字段级别标记要忽略的属性。

让我们使用 @JsonIgnore 来忽略序列化中的属性 id:

public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后我们将测试以确保 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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们还可以测试以确保忽略正常工作:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.4. @JsonInclude

我们可以使用 @JsonInclude 来排除 empty/null/default 的属性。

让我们来看一个从序列化中排除空值的例子:

@JsonInclude(Include.NON_NULL)
public class MyBean {
    public int id;
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

以下是完整的测试:

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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.5. @JsonAutoDetect

@JsonAutoDetect 可以覆盖哪些属性可见哪些不可见的默认语义。

首先,让我们通过一个简单的例子来看看注解是如何发挥作用的; 让我们启用序列化私有属性:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
    private int id;
    private String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

然后测试:

@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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

当我们进行序列化时:

@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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

下面是用Dog序列化Zoo实例的结果:

{
    "animal": {
        "type": "dog",
        "name": "lacy",
        "barkVolume": 0
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

现在开始反序列化。让我们从以下JSON输入开始:

{
    "animal":{
        "name":"lacy",
        "type":"cat"
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后让我们看看如何将它反序列化到 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());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

6. Jackson 常用注解

接下来让我们讨论Jackson的一些更常用的注解。

6.1. @JsonProperty

我们可以添加 @JsonProperty 注解来表示JSON中的属性名。

当我们处理非标准的getter和setter时,让我们使用 @JsonProperty序列化/反序列化属性name:

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;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

接下来是我们的测试:

@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());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6.2. @JsonFormat

@JsonFormat 注解指定序列化 日期/时间 值时的格式。

在下面的例子中,我们使用 @JsonFormat 来控制属性 eventDate 的格式:

public class EventWithFormat {
    public String name;

    @JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

接下来是测试:

@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));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

现在让我们序列化这个类的一个实例:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最后,下面是输出的样子—静态嵌套类的字段和其他字段一起展开:

{
    "id":1,
    "firstName":"John",
    "lastName":"Doe"
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

6.4. @JsonView

@JsonView表示将在其中包含用于序列化/反序列化的属性的视图。

例如,我们将使用 @JsonView 来序列化 Item 实体的实例。

首先,让我们从视图开始:

public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}
 
  • 1
  • 2
  • 3
  • 4

接下来是使用视图的 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;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

最后,完整的测试:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6.5. @JsonManagedReference@JsonBackReference

@JsonManagedReference@JsonBackReference 注解可以处理 父/子 关系并绕过循环。

在下面的例子中,我们使用 @JsonManagedReference@JsonBackReference 来序列化我们的 ItemWithRef 实体:

public class ItemWithRef {
    public int id;
    public String itemName;

    @JsonManagedReference
    public UserWithRef owner;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们的 UserWithRef 实体:

public class UserWithRef {
    public int id;
    public String name;

    @JsonBackReference
    public List<ItemWithRef> userItems;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后测试:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

UserWithIdentity 实体:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class UserWithIdentity {
    public int id;
    public String name;
    public List<ItemWithIdentity> userItems;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

现在让我们看看如何处理无限递归问题:

@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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面是序列化项和用户的完整输出:

{
    "id": 2,
    "itemName": "book",
    "owner": {
        "id": 1,
        "name": "John",
        "userItems": [
            2
        ]
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

6.7. @JsonFilter

注解 @JsonFilter 指定了在序列化期间要使用的过滤器。

首先,我们定义实体并指向过滤器:

@JsonFilter("myFilter")
public class BeanWithFilter {
    public int id;
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5

现在在完整的测试中,我们定义了过滤器,它从序列化中排除除 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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

7. Jackson 自定义注解

接下来,让我们看看如何创建自定义Jackson注解。我们可以使用 @JacksonAnnotationsInside 注解:

@Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonInclude(Include.NON_NULL)
    @JsonPropertyOrder({ "name", "id", "dateCreated" })
    public @interface CustomAnnotation {}
 
  • 1
  • 2
  • 3
  • 4
  • 5

现在,如果我们在实体上使用新注解:

@CustomAnnotation
public class BeanWithCustomAnnotation {
    public int id;
    public String name;
    public Date dateCreated;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们可以看到它如何将现有的注解组合成一个简单的自定义注解,我们可以使用它作为简写:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

序列化过程的输出:

{
    "name":"My bean",
    "id":1
}
 
  • 1
  • 2
  • 3
  • 4

8. Jackson MixIn注解

接下来让我们看看如何使用Jackson MixIn注解。

例如,让我们使用MixIn注解来忽略User类型的属性:

public class Item {
    public int id;
    public String itemName;
    public User owner;
}

@JsonIgnoreType
public class MyMixInForIgnoreType {}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

让我们来看看它的实际应用:

@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")));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

9. 禁用Jackson注解

最后,让我们看看如何禁用所有 Jackson 注解。 我们可以通过禁用 MapperFeature.USE_ANNOTATIONS 来做到这一点,如下例所示:

@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在,在禁用注解之后,这些应该没有效果,并且应该应用库的默认值:

@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"));
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

禁用注解之前序列化的结果:

{"id":1}
 
  • 1

禁用注解后的序列化结果:

{
    "id":1,
    "name":null
}
 
  • 1
  • 2
  • 3
  • 4

<<<<<<<<<<<< [完] >>>>>>>>>>>>

 

标签:Jackson,String,public,bean,详解,result,new,Annotations,name
From: https://www.cnblogs.com/wanghengbin/p/17923192.html

相关文章

  • Spring的BeanDefinitionRegistryPostProcessor接口详解
    BeanDefinitionRegistryPostProcessor介绍BeanDefinitionRegistryPostProcessor它是Spring框架的一个扩展点,用于对Bean定义的注册过程进行干预和定制,例如添加,修改或删除Bean定义等。BeanDefinitionRegistryPostProcessor它继承BeanFactoryPostProcessor接口,并在其基础上扩展了......
  • 软件测试/测试开发|Linux sed命令详解
    sed命令介绍sed是streameditor(流编辑器)的简写,sed可依照脚本的指令来处理、编辑文本文件。Sed主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。sed命令语法基本语法:sed[选项]'动作'文件名常用参数-n,--quiet,--silent取消自动打印模式空间-e......
  • FLV格式详解
    本文是什么FLV(FlashVideo)是由Adobe公司开发的一种非常流行的视频格式,其简单的内容格式非常适合用于流媒体。虽然现如今这种格式已经开始被弃用,但因为各种原因,一部分企业仍然继续使用这种格式来进行流媒体传输。学习FLV格式也有利于学习同样是Adobe公司开发的实时传输协议RTM......
  • procmeminfo 各字段详解
    procmeminfo各字段详解$cat/proc/meminfoMemTotal:877368kB:所有可用RAM大小(即物理内存减去一些预留位和内核的二进制代码大小)(HighTotal+LowTotal),系统从加电开始到引导完成,BIOS等要保留一些内存,内核要保留一些内存,最后剩下可供系统支配的内存就是MemTotal。这......
  • Vite项目中的代理设置详解
    导语在开发Web应用程序时,经常需要与后端API进行通信。然而,由于跨域限制,我们可能会遇到一些问题。Vite是一个快速的构建工具,它提供了一种简单的方式来设置代理,以解决跨域问题。本文将详细介绍如何在Vite项目中设置代理。什么是代理?代理是一种服务器,它充当客户端和目标服务器之间......
  • ThreadLocal和InheritableThreadLocal详解,基本原理及注意项 父子线程数据共享
    一、ThreadLocal介绍在多线程环境下访问同一个线程的时候会出现并发问题,特别是多个线程同时对一个变量进行写入操作时,为了保证线程的安全,通常会进行加锁来保证线程的安全,但是加锁又会造成效率的降低;ThreadLocal是jdk提供的除了加锁之外保证线程安全的方法,其实现原理是在Thread类......
  • Hive-mapjoin详解(mapjoin原理)
    笼统的说,Hive中的Join可分为CommonJoin(Reduce阶段完成join)和MapJoin(Map阶段完成join)。本文简单介绍一下两种join的原理和机制。一.CommonJoin如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成CommonJoin,即在Reduce阶段完成join。CommonJoin整个......
  • k8s组件、工作原理详解
    1.k8s组件  Master组件:kube-apiserver(APIServer):角色:提供集群的唯一入口,处理所有API请求。原理:接收来自客户端(kubectl、UI界面)和其他组件的请求,验证和授权请求,然后将其转发到其他组件或更新etcd中的数据。etcd:角色:分布式键值存储,保存整个集群的状......
  • 详解 MoE
    详解MoE随着Mixtral8x7B的发布(公告,模型卡),MoEtransformer(MixtureofExperts,混合专家)模型已经成为开放AI社区的热门话题。本文,我们主要讨论MoE模型的基础模块、训练方式以及针对推理场景的主要考量。我们开始吧!目录详解MoE目录太长不看版MoE模型到底是什么?MoE......
  • Unity3D iOS 系统与 Unity 交互中如何实现参数传递详解
    在Unity3D开发中,与iOS系统的交互是非常常见的需求。而在交互过程中,参数传递是非常重要的环节。本文将详细介绍在Unity3DiOS系统与Unity交互中如何实现参数传递,并给出技术详解以及代码实现。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事......