More Jackson Annotations – 更多Jackson的注释

最后修改: 2016年 3月 22日


1. Overview


This article covers some additional annotations that were not covered in the previous article, A Guide to Jackson Annotations – we will go through seven of these.


2. @JsonIdentityReference


@JsonIdentityReference is used for customization of references to objects that will be serialized as object identities instead of full POJOs. It works in collaboration with @JsonIdentityInfo to force usage of object identities in every serialization, different from all-but-the-first-time when @JsonIdentityReference is absent. This couple of annotations is most helpful when dealing with circular dependencies among objects. Please refer to section 4 of the Jackson – Bidirectional Relationship article for more information.

@JsonIdentityReference用于定制对象的引用,这些对象将被序列化为对象身份而不是完整的POJO。它与@JsonIdentityInfo协同工作,在每次序列化中强制使用对象身份,当@JsonIdentityReference不存在时,它不同于所有的首次使用。在处理对象之间的循环依赖关系时,这对注解是最有帮助的。请参考Jackson – Bidirectional Relationship文章的第4节以了解更多信息。

In order to demonstrate the use @JsonIdentityReference, we will define two different bean classes, without and with this annotation.


The bean without @JsonIdentityReference:


@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class BeanWithoutIdentityReference {
    private int id;
    private String name;

    // constructor, getters and setters

For the bean using @JsonIdentityReference, we choose the id property to be the object identity:


@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@JsonIdentityReference(alwaysAsId = true)
public class BeanWithIdentityReference {
    private int id;
    private String name;
    // constructor, getters and setters

In the first case, where @JsonIdentityReference is absent, that bean is serialized with full details on its properties:


BeanWithoutIdentityReference bean 
  = new BeanWithoutIdentityReference(1, "Bean Without Identity Reference Annotation");
String jsonString = mapper.writeValueAsString(bean);

The output of the serialization above:


    "id": 1,
    "name": "Bean Without Identity Reference Annotation"

When @JsonIdentityReference is used, the bean is serialized as a simple identity instead:


BeanWithIdentityReference bean 
  = new BeanWithIdentityReference(1, "Bean With Identity Reference Annotation");
String jsonString = mapper.writeValueAsString(bean);
assertEquals("1", jsonString);

3. @JsonAppend


The @JsonAppend annotation is used to add virtual properties to an object in addition to regular ones when that object is serialized. This is necessary when we want to add supplementary information directly into a JSON string, rather than changing the class definition. For instance, it might be more convenient to insert the version metadata of a bean to the corresponding JSON document than to provide it with an additional property.

@JsonAppend 注解用于在一个对象被序列化时,除了常规属性外,还向该对象添加虚拟属性。当我们想把补充信息直接添加到JSON字符串中,而不是改变类的定义时,这是必要的。例如,在相应的JSON文档中插入Bean的version元数据可能比为它提供一个额外的属性更方便。

Assume we have a bean without @JsonAppend as follows:


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

    // constructor, getters and setters

A test will confirm that in the absence of the @JsonAppend annotation, the serialization output does not contain information on the supplementary version property, despite the fact that we attempt to add to the ObjectWriter object:


BeanWithoutAppend bean = new BeanWithoutAppend(2, "Bean Without Append Annotation");
ObjectWriter writer 
  = mapper.writerFor(BeanWithoutAppend.class).withAttribute("version", "1.0");
String jsonString = writer.writeValueAsString(bean);

The serialization output:


    "id": 2,
    "name": "Bean Without Append Annotation"

Now, let’s say we have a bean annotated with @JsonAppend:


@JsonAppend(attrs = { 
  @JsonAppend.Attr(value = "version") 
public class BeanWithAppend {
    private int id;
    private String name;

    // constructor, getters and setters

A similar test to the previous one will verify that when the @JsonAppend annotation is applied, the supplementary property is included after serialization:


BeanWithAppend bean = new BeanWithAppend(2, "Bean With Append Annotation");
ObjectWriter writer 
  = mapper.writerFor(BeanWithAppend.class).withAttribute("version", "1.0");
String jsonString = writer.writeValueAsString(bean);

The output of that serialization shows that the version property has been added:


    "id": 2,
    "name": "Bean With Append Annotation",
    "version": "1.0"

4. @JsonNaming


The @JsonNaming annotation is used to choose the naming strategies for properties in serialization, overriding the default. Using the value element, we can specify any strategy, including custom ones.

@JsonNaming 注解用于选择序列化中属性的命名策略,覆盖默认值。使用value元素,我们可以指定任何策略,包括自定义策略。

In addition to the default, which is LOWER_CAMEL_CASE (e.g. lowerCamelCase), Jackson library provides us with four other built-in property naming strategies for convenience:


  • KEBAB_CASE: Name elements are separated by hyphens, e.g. kebab-case.
  • LOWER_CASE: All letters are lowercase with no separators, e.g. lowercase.
  • SNAKE_CASE: All letters are lowercase with underscores as separators between name elements, e.g. snake_case.
  • UPPER_CAMEL_CASE: All name elements, including the first one, start with a capitalized letter, followed by lowercase ones and there are no separators, e.g. UpperCamelCase.

This example will illustrate the way to serialize properties using snake case names, where a property named beanName is serialized as bean_name.


Given a bean definition:


public class NamingBean {
    private int id;
    private String beanName;

    // constructor, getters and setters

The test below demonstrates that the specified naming rule works as required:


NamingBean bean = new NamingBean(3, "Naming Bean");
String jsonString = mapper.writeValueAsString(bean);        
assertThat(jsonString, containsString("bean_name"));

The jsonString variable contains following data:


    "id": 3,
    "bean_name": "Naming Bean"

5. @JsonPropertyDescription


The Jackson library is able to create JSON schemas for Java types with the help of a separate module called JSON Schema. The schema is useful when we want to specify expected output when serializing Java objects, or to validate a JSON document before deserialization.

Jackson库能够在一个名为JSON Schema的独立模块的帮助下为Java类型创建JSON模式。当我们想在序列化Java对象时指定预期的输出,或者在反序列化之前验证JSON文档时,该模式非常有用。

The @JsonPropertyDescription annotation allows a human readable description to be added to the created JSON schema by providing the description field.


This section makes use of the bean declared below to demonstrate the capabilities of @JsonPropertyDescription:


public class PropertyDescriptionBean {
    private int id;
    @JsonPropertyDescription("This is a description of the name property")
    private String name;

    // getters and setters

The method for generating a JSON schema with the addition of the description field is shown below:


SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(PropertyDescriptionBean.class, wrapper);
JsonSchema jsonSchema = wrapper.finalSchema();
String jsonString = mapper.writeValueAsString(jsonSchema);
assertThat(jsonString, containsString("This is a description of the name property"));

As we can see, the generation of JSON schema was successful:


    "type": "object",
    "id": "urn:jsonschema:com:baeldung:jackson:annotation:extra:PropertyDescriptionBean",
            "type": "string",
            "description": "This is a description of the name property"

            "type": "integer"

6. @JsonPOJOBuilder


The @JsonPOJOBuilder annotation is used to configure a builder class to customize deserialization of a JSON document to recover POJOs when the naming convention is different from the default.


Suppose we need to deserialize the following JSON string:


    "id": 5,
    "name": "POJO Builder Bean"

That JSON source will be used to create an instance of the POJOBuilderBean:


@JsonDeserialize(builder = BeanBuilder.class)
public class POJOBuilderBean {
    private int identity;
    private String beanName;

    // constructor, getters and setters

The names of the bean’s properties are different from those of the fields in JSON string. This is where @JsonPOJOBuilder comes to the rescue.


The @JsonPOJOBuilder annotation is accompanied by two properties:


  • buildMethodName: The name of the no-arg method used to instantiate the expected bean after binding JSON fields to that bean’s properties. The default name is build.
  • withPrefix: The name prefix for auto-detection of matching between the JSON and bean’s properties. The default prefix is with.

This example makes use of the BeanBuilder class below, which is used on POJOBuilderBean:


@JsonPOJOBuilder(buildMethodName = "createBean", withPrefix = "construct")
public class BeanBuilder {
    private int idValue;
    private String nameValue;

    public BeanBuilder constructId(int id) {
        idValue = id;
        return this;

    public BeanBuilder constructName(String name) {
        nameValue = name;
        return this;

    public POJOBuilderBean createBean() {
        return new POJOBuilderBean(idValue, nameValue);

In the code above, we have configured the @JsonPOJOBuilder to use a build method called createBean and the construct prefix for matching properties.


The application of @JsonPOJOBuilder to a bean is described and tested as follows:


String jsonString = "{\"id\":5,\"name\":\"POJO Builder Bean\"}";
POJOBuilderBean bean = mapper.readValue(jsonString, POJOBuilderBean.class);

assertEquals(5, bean.getIdentity());
assertEquals("POJO Builder Bean", bean.getBeanName());

The result shows that a new data object has been successfully re-created from a JSON source in despite a mismatch in properties’ names.


7. @JsonTypeId


The @JsonTypeId annotation is used to indicate that the annotated property should be serialized as the type id when including polymorphic type information, rather than as a regular property. That polymorphic metadata is used during deserialization to recreate objects of the same subtypes as they were before serialization, rather than of the declared supertypes.


For more information on Jackson’s handling of inheritance, see section 2 of the Inheritance in Jackson.


Let’s say we have a bean class definition as follows:


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

    // constructor, getters and setters

The following test validates that @JsonTypeId works as it is meant to:


TypeIdBean bean = new TypeIdBean(6, "Type Id Bean");
String jsonString = mapper.writeValueAsString(bean);
assertThat(jsonString, containsString("Type Id Bean"));

The serialization process’ output:


    "Type Id Bean",
        "id": 6

8. @JsonTypeIdResolver


The @JsonTypeIdResolver annotation is used to signify a custom type identity handler in serialization and deserialization. That handler is responsible for conversion between Java types and type id included in a JSON document.

@JsonTypeIdResolver 注解用于表示序列化和反序列化中的自定义类型标识处理器。该处理程序负责Java类型和JSON文档中的类型标识之间的转换。

Suppose that we want to embed type information in a JSON string when dealing with the following class hierarchy.


The AbstractBean superclass:


  use = JsonTypeInfo.Id.NAME, 
  include = JsonTypeInfo.As.PROPERTY, 
  property = "@type"
public class AbstractBean {
    private int id;

    protected AbstractBean(int id) { = id;

    // no-arg constructor, getter and setter

The FirstBean subclass:


public class FirstBean extends AbstractBean {
    String firstName;

    public FirstBean(int id, String name) {

    // no-arg constructor, getter and setter

The LastBean subclass:


public class LastBean extends AbstractBean {
    String lastName;

    public LastBean(int id, String name) {

    // no-arg constructor, getter and setter

Instances of those classes are used to populate a BeanContainer object:


public class BeanContainer {
    private List<AbstractBean> beans;

    // getter and setter

We can see that the AbstractBean class is annotated with @JsonTypeIdResolver, indicating that it uses a custom TypeIdResolver to decide how to include subtype information in serialization and how to make use of that metadata the other way round.


Here is the resolver class to handle inclusion of type information:


public class BeanIdResolver extends TypeIdResolverBase {
    private JavaType superType;

    public void init(JavaType baseType) {
        superType = baseType;

    public Id getMechanism() {
        return Id.NAME;

    public String idFromValue(Object obj) {
        return idFromValueAndType(obj, obj.getClass());

    public String idFromValueAndType(Object obj, Class<?> subType) {
        String typeId = null;
        switch (subType.getSimpleName()) {
        case "FirstBean":
            typeId = "bean1";
        case "LastBean":
            typeId = "bean2";
        return typeId;

    public JavaType typeFromId(DatabindContext context, String id) {
        Class<?> subType = null;
        switch (id) {
        case "bean1":
            subType = FirstBean.class;
        case "bean2":
            subType = LastBean.class;
        return context.constructSpecializedType(superType, subType);

The two most notable methods are idFromValueAndType and typeFromId, with the former telling the way to include type information when serializing POJOs and the latter determining the subtypes of re-created objects using that metadata.


In order to make sure that both serialization and deserialization work well, let’s write a test to validate the complete progress.


First, we need to instantiate a bean container and bean classes, then populate that container with bean instances:


FirstBean bean1 = new FirstBean(1, "Bean 1");
LastBean bean2 = new LastBean(2, "Bean 2");

List<AbstractBean> beans = new ArrayList<>();

BeanContainer serializedContainer = new BeanContainer();

Next, the BeanContainer object is serialized and we confirm that the resulting string contains type information:


String jsonString = mapper.writeValueAsString(serializedContainer);
assertThat(jsonString, containsString("bean1"));
assertThat(jsonString, containsString("bean2"));

The output of serialization is shown below:


            "@type": "bean1",
            "id": 1,
            "firstName": "Bean 1"

            "@type": "bean2",
            "id": 2,
            "lastName": "Bean 2"

That JSON structure will be used to re-create objects of the same subtypes as before serialization. Here are the implementation steps for deserialization:


BeanContainer deserializedContainer = mapper.readValue(jsonString, BeanContainer.class);
List<AbstractBean> beanList = deserializedContainer.getBeans();
assertThat(beanList.get(0), instanceOf(FirstBean.class));
assertThat(beanList.get(1), instanceOf(LastBean.class));

9. Conclusion


This tutorial has explained several less-common Jackson annotations in detail. The implementation of these examples and code snippets can be found in a GitHub project.