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,並且想要將包含諸如 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 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 輸出中包含 name 和 email 屬性,你可以使用以下標註:
@JsonInclude(JsonInclude.Include.NonZero)
public class User {
private String name;
private String email;
private int age;
// ...
}
這將確保 JSON 輸出中只包含 name 和 email 屬性,而 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
}