Jackson Annotation 示例

概述

在本教程中,我们将深入研究 Jackson Annotations。
我们将了解如何使用现有注解,如何创建自定义注解,最后如何禁用它们。

Jackson 序列化注解

@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,并在序列化后显示在属性变量下。

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

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

@JsonRawValue

@JsonRawValue 注解可以指示Jackson完全按原样序列化属性。
在下面的示例中,我们使用 @JsonRawValue 将一些自定义 JSON 作为实体的值嵌入:

public class RawBean {
    public String name;

    @JsonRawValue
    public String json;
}

序列化实体的输出如下:

{
    "name":"My bean",
    "json":{
        "attr":false
    }
}

下面是一个简单的测试:

@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
  throws JsonProcessingException {
 
    RawBean bean = new RawBean("My bean", "{\"attr\":false}");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("{\"attr\":false}"));
}

我们还可以使用可选的 boolean 参数值来定义该注解是否处于激活状态。

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

@JsonRootName

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

包装意味着不要将 User 序列化为如下所示的内容:

{
    "id": 1,
    "name": "John"
}

它将被包装成这样:

{
    "User": {
        "id": 1,
        "name": "John"
    }
}

让我们看一个例子。我们将使用 @JsonRootName 来指示此潜在包装器实体的名称:

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

默认情况下,包装器的名称将是类的名称 – UserWithRoot。通过使用注释,我们得到了看起来更干净的 User:

@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
  throws JsonProcessingException {
 
    UserWithRoot user = new User(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    String result = mapper.writeValueAsString(user);

    assertThat(result, containsString("John"));
    assertThat(result, containsString("user"));
}

下面是序列化的输出:

{
    "user":{
        "id":1,
        "name":"John"
    }
}

从Jackson 2.4开始,新的可选参数命名空间可用于诸如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>

@JsonSerialize

@JsonSerialize 指示在封送实体时使用的自定义序列化程序。
让我们来看一个简单的例子。
我们将使用 @JsonSerialize 将 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));
}

Jackson 反序列化注解

接下来,让我们探索 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

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

@JsonSetter

@JsonSetter 是 @JsonProperty 的替代方法,它将方法标记为 setter 方法。
当我们需要读取一些 JSON 数据时,这是非常有用的,但目标实体类与该数据不完全匹配,因此我们需要调优流程以使其适合。
在下面的示例中,我们将方法 setTheName() 指定为 MyBean 实体中的 name 属性的 setter:

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

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }
}

现在,当我们需要反序列化一些JSON数据时,这可以很好地工作:

@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
  throws IOException {
 
    String json = "{\"id\":1,\"name\":\"My bean\"}";

    MyBean bean = new ObjectMapper()
      .readerFor(MyBean.class)
      .readValue(json);
    assertEquals("My bean", bean.getTheName());
}

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

@JsonAlias

@JsonAlias在反序列化过程中为属性定义一个或多个替代名称。

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

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

这里我们有一个POJO,我们希望将带有fName、f_name和 firstName 等值的JSON反序列化为POJO的 firstName变量。

以下是确保该注释按预期工作的测试:

@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
    String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
    AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
    assertEquals("John", aliasBean.getFirstName());
}

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

要无一例外地忽略JSON输入中的任何未知属性,我们可以将@JsonIgnoreProperties批注设置为IgnreUnnow=true

@JsonIgnore

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

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

public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}

然后,我们将进行测试以确保该 id 被成功忽略:

@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
  throws JsonProcessingException {
 
    BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

    String result = new ObjectMapper()
      .writeValueAsString(bean);
    
    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

@JsonIgnoreType

@JsonIgnoreType标记要忽略的带注释类型的所有属性。
我们可以使用注释来标记要忽略的类型名称的所有属性:

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 排 empty/null/default 的属性。
让我们看一个从序列化中排除空值的示例:

@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可以覆盖哪些属性可见、哪些属性不可见的默认语义。
首先,让我们通过一个简单的例子来看一下注释是如何非常有用的;让我们来启用序列化私有属性:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
    private int id;
    private String name;
}

Then the test:

@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 通用注解

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

@JsonProperty

我们可以添加@JsonProperty注释来指示JSON中的属性名称。
在处理非标准的getter和setter时,让我们使用@JsonProperty来序列化/反序列化属性名称:

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

@JsonFormat

@JsonFormat注释指定序列化日期/时间值时的格式。
在下面的示例中,我们使用@JsonFormat控制属性EventDate的格式:

public class EventWithFormat {
    public String name;

    @JsonFormat(
      shape = JsonFormat.Shape.STRING,
      pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}
然后是测试:

@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
  throws JsonProcessingException, ParseException {
    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    EventWithFormat event = new EventWithFormat("party", date);
    
    String result = new ObjectMapper().writeValueAsString(event);
    
    assertThat(result, containsString(toParse));
}

@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”
}

@JsonView

@JsonView 指示将在其中包含属性以进行序列化或反序列化的View。
例如,我们将使用@JsonView来序列化Item Entity的实例。
首先,让我们从视图开始:

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

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

@JsonIdentityInfo

@JsonIdentityInfo指示在序列化或反序列化值时应使用对象标识,例如,在处理无限递归类型的问题时。
在下面的示例中,我们有一个与 UserWithIdentity 具有双向关系的 ItemWithIdentity 实体:

@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
        ]
    }
}

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

自定义 Jackson 注解

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

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id", "dateCreated" })
public @interface CustomAnnotation {}

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

@CustomAnnotation
public class BeanWithCustomAnnotation {
    public int id;
    public String name;
    public Date dateCreated;
}

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

@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
  throws JsonProcessingException {
    BeanWithCustomAnnotation bean 
      = new BeanWithCustomAnnotation(1, "My bean", null);

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("dateCreated")));
}

序列化过程的输出:

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

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

关闭 Jackson 注解

最后,让我们看看如何禁用所有Jackson注释。
我们可以通过禁用MapperFeature.USE_Annotation来实现这一点,如下例所示:

@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 中,可以通过配置 `ObjectMapper` 来实现将 Java 对象的属性名从驼峰形式转换为下划线形式的命名方式,这个过程也称为“蛇形命名法”(Snake Case)。 可以通过添加 `PropertyNamingStrategy` 实现,例如,下面是一个将驼峰命名转换为下划线命名的策略示例: ```java import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) public class MyObject { private String myProperty; // getters and setters } ``` 在上面的示例中,`@JsonNaming` 注解指定了使用 `SnakeCaseStrategy` 策略,这会将属性名 `myProperty` 转换为 `my_property`。 如果需要自定义命名策略,只需要实现 `PropertyNamingStrategy` 接口即可。例如,下面是一个将驼峰命名转换为大写下划线命名的策略示例: ```java import com.fasterxml.jackson.databind.PropertyNamingStrategy; public class UpperCaseSnakeCaseStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase { @Override public String translate(String input) { if (input == null) { return input; } StringBuilder result = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); if (Character.isUpperCase(ch)) { result.append("_"); } result.append(Character.toLowerCase(ch)); } return result.toString().toUpperCase(); } } ``` 使用方式与上面类似: ```java @JsonNaming(UpperCaseSnakeCaseStrategy.class) public class MyObject { private String myProperty; // getters and setters } ``` 这个策略会将属性名 `myProperty` 转换为 `MY_PROPERTY`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值