Jackson Annotations(二)


接着上一篇【Jackson Annotations(一)】,接着再介绍Jackson Property Inclusion Annotations和一些更加普遍的注解。为了方便,这里面很多直接用了public属性。

Jackson Property Inclusion Annotations

@JsonIgnoreProperties

@JsonIgnoreProperties 标识jackson序列化或反序列化将忽略的属性。

@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
    public int id;
    public String name;

    public BeanWithIgnore() {
    }

    public BeanWithIgnore(final int id, final String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "BeanWithIgnore{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

序列化

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

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

输出结果:

{"name":"My bean"}

反序列化

@Test
    public void whenSerializingUsingJsonIgnoreProperties1() throws IOException {
        String json = "{\"id\": 1, \"name\":\"My bean\"}";
        BeanWithIgnore beanWithIgnore = new ObjectMapper().readerFor(BeanWithIgnore.class).readValue(json);
        System.out.println(beanWithIgnore);
    }
BeanWithIgnore{id=0, name='null'}

@JsonIgnore

标明的field, getter/setter method or Creator parameter等在序列化或者发序列化时将被忽略。
将上面例子的实体修改一下用@JsonIgnore,测试代码不变,可以得到相同的结果。

//@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
    @JsonIgnore
    public int id;
    public String name;

    public BeanWithIgnore() {

    }

    public BeanWithIgnore(final int id, final String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "BeanWithIgnore{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

@JsonIgnoreType

@JsonIgnoreType 标识jackson序列化或反序列化将忽略某些类型的属性。

public class Teacher {
    public int id;
    public Name name;
    
    public Teacher() {
    }
    public Teacher(final int id, final Name name) {
        this.id = id;
        this.name = name;
    }

    @JsonIgnoreType
    public static class Name {
        public String firstName;
        public String lastName;
        
        public Name() {
        }
        public Name(final String firstName, final String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
}
@Test
    public void whenSerializingUsingJsonIgnoreType()
            throws JsonProcessingException, ParseException {

        Teacher.Name name = new Teacher.Name("John", "Doe");
        Teacher teacher = new Teacher(1, name);

        String result = new ObjectMapper().writeValueAsString(teacher);
        System.out.println(result);
    }

输出结果:

{"id":1}

@JsonInclude

使用@JsonInclude注解可以控制field, method or constructor parameter的序列化。例如下面,我们排出序列化时的null值。

@JsonInclude(JsonInclude.Include.NON_NULL)
public class IncludeBean {
    public int id;
    public String name;

    public IncludeBean() {

    }

    public IncludeBean(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
}
@Test
    public void whenSerializingUsingJsonInclude() throws JsonProcessingException {
        IncludeBean bean = new IncludeBean(1, null);

        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result);
    }

输出结果:

{"id":1}

可以看到name属性没有被序列化.

@JsonAutoDetect

修改属性自动检测
默认的Jackson属性检测规则将找到:

  • 所有的public类型的属性
  • 所有的public类型的get方法
  • 所有的set方法(无论方法的可见性)

如果默认规则不能满足需求,可以用@JsonAutoDetect注解来改变。

  1. 如果想检测所有属性,可以
    @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
    public class POJOWithFields {
      private int value;
    }
  2. 如果想禁止检测所有属性
    @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.NONE)
    public class POJOWithNoFields {
      // 该属性将不被包含,除非有getValue()方法
      public int value;
    }

    处理多态类型

@JsonTypeInfo,@JsonSubTypes ,@JsonTypeName

如果需要读取和写入具有多个可能子类型的对象的值(即表现出多态性的对象),则可能需要启用包含类型信息。这是必需的,以便Jackson在反序列化时读取正确的对象类型(将JSON读入对象)。这可以通过@JsonTypeInfo在“基类”上添加注释来完成,更详细的信息,可以参考文章下面的链接。

public class Zoo {
    public Animal animal;

    public Zoo() {
    }
    public Zoo(final Animal animal) {
        this.animal = animal;
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.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;

        public Animal() {
        }

        public Animal(final String name) {
            this.name = name;
        }
    }

    public static class Dog extends Animal {
        public double barkVolume;

        public Dog() {
        }

        public Dog(final String name) {
            this.name = name;
        }
    }

    public static class Cat extends Animal {
        boolean likesCream;
        public int lives;

        public Cat() {
        }

        public Cat(final String name) {
            this.name = name;
        }
    }
}

序列化测试:

@Test
    public void whenSerializingPolymorphic() throws JsonProcessingException {
        Zoo.Dog dog = new Zoo.Dog("lacy");
        Zoo zoo = new Zoo(dog);

        String result = new ObjectMapper().writeValueAsString(zoo);
        System.out.println(result);
    }

输出结果:

{
    "animal":{
        "type":"dog",
        "name":"lacy",
        "barkVolume":0
    }
}

反序列化测试:

@Test
    public void whenDeserializingPolymorphic() 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());
    }

该测试会success.

更普遍使用的注解

@JsonProperty

作用在字段或方法上,用来对属性的序列化/反序列化,可以用来提供对属性名称重命名,处理非标准的GetterSetter

public class JsonPropertyBean {

    public int id;

    private String name;

    public JsonPropertyBean() {

    }

    public JsonPropertyBean(final int id, final String name) {
        this.id = id;
        this.name = name;
    }

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

    @JsonProperty("name")
//    @JsonGetter("name")
    public String getTheName() {
        return name;
    }
}
@Test
    public void whenUsingJsonProperty() throws IOException {
        JsonPropertyBean bean = new JsonPropertyBean(1, "My bean");

        String result = new ObjectMapper().writeValueAsString(bean);
        // {"id":1,"name":"My bean"}
        System.out.println(result);

        JsonPropertyBean resultBean = new ObjectMapper().readerFor(JsonPropertyBean.class)
                .readValue(result);
        assertEquals("My bean", resultBean.getTheName());
    }

@JsonFormat

当序列化时指定Date/Time的格式化。

public class DateFormatBean {

    @JsonFormat(pattern="yyyy-MM-dd hh:mm:ss", timezone="GMT+8")
    public Date birth;

    public DateFormatBean() {}

    public DateFormatBean(Date birth) {
        this.birth = birth;
    }
}
@Test
    public void whenSerializingUsingJsonFormat() throws JsonProcessingException {
        DateFormatBean bean = new DateFormatBean(new Date());
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result);
    }

输出结果(如果不加该注解,默认输出毫秒时间戳),

{"birth":"2019-04-28 11:01:04"}

@JsonUnwrapped

@JsonUnwrapped定义了序列化/反序列化时应解包/展平的值。看下面的例子:

public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    public UnwrappedUser() {

    }

    public UnwrappedUser(final int id, final Name name) {
        this.id = id;
        this.name = name;
    }

    public static class Name {
        public String firstName;
        public String lastName;

        public Name() {

        }

        public Name(final String firstName, final String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
}
@Test
    public void whenSerializingUsingJsonUnwrapped() throws JsonProcessingException, ParseException {
        UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
        UnwrappedUser user = new UnwrappedUser(1, name);

        String result = new ObjectMapper().writeValueAsString(user);
        System.out.println(result);
        assertThat(result, containsString("John"));
        assertThat(result, not(containsString("name")));
    }

输出结果,静态内部类的字段和其他字段一起平铺展开:

{
    "id":1,
    "firstName":"John",
    "lastName":"Doe"
}

@JsonView

@JsonView可以用来指定序列化/反序列化包含的属性,下面,看一下具体的例子。

public class Views {
    public static class Public {
    }
    public static class Internal extends Public {
    }
}
public class Item {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Internal.class)
    public String ownerName;

    public Item() {
        super();
    }

    public Item(int id, String itemName, String ownerName) {
        this.id = id;
        this.itemName = itemName;
        this.ownerName = ownerName;
    }

    public int getId() {
        return id;
    }

    public String getItemName() {
        return itemName;
    }

    public String getOwnerName() {
        return 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);
        System.out.println(result);

        String result1 = new ObjectMapper().writerWithView(Views.Internal.class)
                .writeValueAsString(item);
        System.out.println(result1);
    }

​ result打印结果:

{
    "id":2,
    "itemName":"book"
}

result1j打印结果:

{
    "id":2,
    "itemName":"book",
    "ownerName":"John"
}

@JsonManagedReference,@ JsonBackReference

@JsonManagedReference和@JsonBackReference注释可以处理父/子关系和解决循环问题。这两个注解在使用的时候,@JsonBackReference标注的属性不会被序列化,从而解决循环引用无法序列化问题。

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

    @JsonManagedReference
    public UserWithRef owner;

    public ItemWithRef() {
        super();
    }

    public ItemWithRef(int id, String itemName, UserWithRef owner) {
        this.id = id;
        this.itemName = itemName;
        this.owner = owner;
    }
}

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

    @JsonBackReference
    public ItemWithRef item;

    public UserWithRef() {
        super();
    }

    public UserWithRef(int id, String name) {
        this.id = id;
        this.name = name;
    }

}
@Test
    public void whenSerializingUsingJacksonReferenceAnnotation() throws JsonProcessingException {
        UserWithRef user = new UserWithRef(1, "John");
        ItemWithRef item = new ItemWithRef(2, "book", user);
        user.item = item;
      
        String result = new ObjectMapper().writeValueAsString(item);
        System.out.println(result);
    }

输出结果:

{
    "id":2,
    "itemName":"book",
    "owner":{
        "id":1,
        "name":"John"
    }
}

@JsonIdentityInfo

使用这个注解也可以用来解决循环引用问题,不像前两个注解那用序列化时忽略某个属性。看下面的例子:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ItemWithIdentity {
    public int id;
    public String itemName;
    public UserWithIdentity owner;

    public ItemWithIdentity() {
        super();
    }

    public ItemWithIdentity(final int id, final String itemName, final UserWithIdentity owner) {
        this.id = id;
        this.itemName = itemName;
        this.owner = owner;
    }
}

@JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class UserWithIdentity {
    public int id;
    public String name;
    public ItemWithIdentity item;

    public UserWithIdentity() {
        super();
    }

    public UserWithIdentity(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
@Test
    public void whenSerializingUsingJsonIdentityInfo() throws JsonProcessingException {
        UserWithIdentity user = new UserWithIdentity(1, "John");
        ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
        user.item = item;

        String result = new ObjectMapper().writeValueAsString(item);
        System.out.println(result);
    }

输出结果:

{
    "id":2,
    "itemName":"book",
    "owner":{
        "@id":"82d8a603-a4e0-4dd2-b3e5-c2207529c59a",
        "id":1,
        "name":"John",
        "item":2
    }
}

参考:github
参考:Jackson Annotation Examples


文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录