知識庫 / JSON / Jackson RSS 訂閱

Jackson 註解示例

Data,Jackson
HongKong
6
09:57 PM · Dec 05 ,2025

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 註解的替代方案,用於標記方法為獲取器方法。

在下面的示例中,我們指定getTheName() 方法為MyBean 實體中name 屬性的獲取器方法:

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. <em @JsonPropertyOrder</em>

我們可以使用 <em @JsonPropertyOrder</em> 註解來指定序列化時屬性的順序。

讓我們為 <em MyBean</em> 實體設置自定義屬性順序:

@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

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

我們還可以使用可選的布爾參數 ,它定義了該標註是否處於啓用狀態。

2.5. @JsonValue

<em >@JsonValue</em > 表示庫將使用的方法來序列化整個實例。

例如,在枚舉中,我們使用 <em >@JsonValue</em > 註解 <em >getName</em >,以便任何此類實體通過其名稱進行序列化:

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

<em >@JsonRootName</em> 註解用於在啓用包裝的情況下指定用於使用的根包裝器的名稱。

包裝是指,而不是將 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. <em @JsonSerialize

marshalling the entity.

讓我們來看一個簡單的例子。我們將使用 eventDate 屬性序列化,並使用 CustomDateSerializer

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. <em @JsonCreator

我們可以使用 註解來調整反序列化過程中使用的構造函數/工廠。

當我們需要反序列化某些與目標實體不完全匹配的 JSON 時,它非常有用。

讓我們來看一個例子。假設我們需要反序列化以下 JSON:

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

然而,我們的目標實體中沒有 <em >theName</em > 字段,只有一個 <em >name</em > 字段。我們現在不希望改變實體本身,只需要通過在構造函數中添加 <em >@JsonCreator</em > 註解以及使用 <em >@JsonProperty</em > 註解來獲得對反序列化過程的更多控制:

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. <em @JsonAnySetter</em>>

@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 的替代方案,它標記方法為設置方法。

當我們需要讀取一些 JSON 數據,但目標實體類與該數據不完全匹配時,@JsonSetter 極其有用,我們需要調整過程以使其適應。

在下面的示例中,我們將setTheName() 方法指定為MyBean 實體中name 屬性的設置方法:

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. <em @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. <em @JsonAlias@

The defines one or more alternative names for a property during deserialization.

Let’s see how this annotation works with a quick example:

public class AliasBean {
    @JsonAlias({ "fName", "f_name" })
    private String firstName;   
    private String lastName;
}

這裏我們有一個POJO,並且想要將包含諸如 fNamef_namefirstName 值的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 Property Inclusion Annotations

Jackson 的 Property Inclusion 標註(Annotations)允許你控制哪些屬性應該包含在 JSON 序列化/反序列化過程中。 它們提供了精細的控制,可以避免不必要的屬性被包含在輸出中,從而減少數據傳輸量和提高性能。

以下是幾種常用的 Property Inclusion 標註:

  • @JsonInclude:這是最常用的標註,用於指定哪些屬性應該包含在 JSON 輸出中。
    • @JsonInclude(JsonInclude.Include.All):包含所有屬性。
    • @JsonInclude(JsonInclude.Include.NonZero):包含非零值屬性。
    • @JsonInclude(JsonInclude.Include.NonZero):包含非零值屬性。
    • @JsonInclude(JsonInclude.Include.Exclude):排除所有屬性(默認行為)。
  • @JsonIgnore:用於完全忽略某個屬性,無論其值是否為 null。 這種標註通常用於隱藏敏感信息或不希望在 JSON 輸出中包含的屬性。
  • @JsonIgnoreProperties:允許你指定一個或多個屬性應該被忽略。 它可以與單個屬性或數組一起使用。

示例:

假設你有一個包含用户信息的類:

public class User {
    private String name;
    private String email;
    private int age;

    // ...
}

如果你只想在 JSON 輸出中包含 nameemail 屬性,你可以使用以下標註:

@JsonInclude(JsonInclude.Include.NonZero)
public class User {
    private String name;
    private String email;
    private int age;

    // ...
}

這將確保 JSON 輸出中只包含 nameemail 屬性,而 age 屬性將被忽略。

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 輸入中任何未知的屬性,而無需引發異常,我們可以將 ignoreUnknown 設置為 true,應用於 @JsonIgnoreProperties 註解。

4.2. @JsonIgnore

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

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. <em @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;
}

現在,這是一個測試,確保我們只包含 屬性:

@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 註解指定了在序列化 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 屬性:

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. <em @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 註解。 我們可以利用 <em @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 註解,只是利用它們正確的方式,窺探了我們能夠獲得的靈活性的一角。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.