Tech and Media Labs
This site uses cookies to improve the user experience.




Jackson Annotations

Jakob Jenkov
Last update: 2015-11-18

The Jackson JSON toolkit contains a set of Java annotations which you can use to influence how JSON is read into objects, or what JSON is generated from the objects. This Jackson annotation tutorial explains how to use Jackson's annotations.

If you are unfamiliar with Java annotations, read my Java annotation tutorial before reading this tutorial. It will help you understand how Java annotations work.

Read + Write Annotations

Jackson contains a set of annotations that affect both the reading of Java objects from JSON, as well as the writing of Java objects into JSON. I refer to these annotations as "read + write annotations". The following sections explain Jackson's read + write annotations in more detail.

@JsonIgnore

The Jackson annotation @JsonIgnore is used to tell Jackson to ignore a certain property (field) of a Java object. The property is ignored both when reading JSON into Java objects, and when writing Java objects into JSON. Here is an example class that uses the @JsonIgnore annotation:

import com.fasterxml.jackson.annotation.JsonIgnore;

public class PersonIgnore {

    @JsonIgnore
    public long    personId = 0;

    public String  name = null;
}

In the above class the property personId will not be read from JSON or written to JSON.

@JsonIgnoreProperties

The @JsonIgnoreProperties Jackson annotation is used to specify a list of properties of a class to ignore. The @JsonIgnoreProperties annotation is placed above the class declaration instead of above the individual properties (fields) to ignore. Here is an example showing how to use the @JsonIgnoreProperties annotation:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties({"firstName", "lastName"})
public class PersonIgnoreProperties {

    public long    personId = 0;

    public String  firstName = null;
    public String  lastName  = null;

}

In this example both the properties firstName and lastName will be ignored because their names are listed inside the @JsonIgnoreProperties annotation declaration above the class declaration.

@JsonIgnoreType

The @JsonIgnoreType Jackson annotation is used to mark a whole type (class) to be ignored everywhere that type is used. Here is an example that shows you how you could use the @JsonIgnoreType annotation:

import com.fasterxml.jackson.annotation.JsonIgnoreType;

public class PersonIgnoreType {

    @JsonIgnoreType
    public static class Address {
        public String streetName  = null;
        public String houseNumber = null;
        public String zipCode     = null;
        public String city        = null;
        public String country     = null;
    }

    public long    personId = 0;

    public String  name = null;

    public Address address = null;
}

In the above example all Address instances will be ignored.

@JsonAutoDetect

The Jackson annotation @JsonAutoDetect is used to tell Jackson to include properties which are not public, both when reading and writing objects. Here is an example class showing you how you can use the @JsonAutoDetect annotation:

import com.fasterxml.jackson.annotation.JsonAutoDetect;

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
public class PersonAutoDetect {

    private long  personId = 123;
    public String name     = null;

}

The JsonAutoDetect.Visibility class contains constants matching the visibility levels in Java, meaning ANY, DEFAULT, NON_PRIVATE, NONE, PROTECTED_AND_PRIVATE and PUBLIC_ONLY.

Read Annotations

Jackson contains a set of annotations that only affect how Jackson parses JSON into objects - meaning they affect Jackson's reading of JSON. I refer to these as "read annotations". The following sections explains Jackson's read annotations.

@JsonSetter

The Jackson annotation @JsonSetter is used to tell Jackson that is should match the name of this setter method to a property name in the JSON data, when reading JSON into objects. This is useful if the property names used internally in your Java class is not the same as the property name used in the JSON file.

The following Person class uses the name personId for its id property:

public class Person {

    private long   personId = 0;
    private String name     = null;

    public long getPersonId() { return this.personId; }
    public void setPersonId(long personId) { this.personId = personId; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

But in this JSON object the name id is used instead of personId:

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

Without some help Jackson cannot map the id property from the JSON object to the personId field of the Java class.

The @JsonSetter annotation instructs Jackson to use a setter method for a given JSON field. In our case we add the @JsonSetter annotation above the setPersonId() method. Here is how adding the @JsonSetter annotation looks like:

public class Person {

    private long   personId = 0;
    private String name     = null;

    public long getPersonId() { return this.personId; }
    @JsonSetter("id")
    public void setPersonId(long personId) { this.personId = personId; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

The value specified inside the @JsonSetter annotation is the name of the JSON field to match to this setter method. In this case the name is id since that is the name of the field in the JSON object we want to map to the setPersonId() setter method.

@JsonAnySetter

The Jackson annotation @JsonAnySetter instructs Jackson to call the same setter method for all unrecognized fields in the JSON object. By "unrecognized" I mean all fields that are not already mapped to a property or setter method in the Java object. Look at this Bag class:

public class Bag {

    private Map<String, Object> properties = new HashMap<>();

    public void set(String fieldName, Object value){
        this.properties.put(fieldName, value);
    }

    public Object get(String fieldName){
        return this.properties.get(fieldName);
    }
}

And then look at this JSON object:

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

Jackson cannot directly map the id and name property of this JSON object to the Bag class, because the Bag class contains no public fields or setter methods.

You can tell Jackson to call the set() method for all unrecognized fields by adding the @JsonAnySetter annotation, like this:

public class Bag {

    private Map<String, Object> properties = new HashMap<>();

    @JsonAnySetter
    public void set(String fieldName, Object value){
        this.properties.put(fieldName, value);
    }

    public Object get(String fieldName){
        return this.properties.get(fieldName);
    }
}

Now Jackson will call the set() method with the name and value of all unrecognized fields in the JSON object.

Keep in mind that this only has effect on unrecognized fields. If, for instance, you added a public name property or setName(String) method to the Bag Java class, then the name field in the JSON object would be mapped to that property / setter instead.

@JsonCreator

The Jackson annotation @JsonCreator is used to tell Jackson that the Java object has a constructor (a "creator") which can match the fields of a JSON object to the fields of the Java object.

The @JsonCreator annotation is useful in situations where the @JsonSetter annotation cannot be used. For instance, immutable objects do not have any setter methods, so they need their initial values injected into the constructor. Look at this PersonImmutable class as example:

public class PersonImmutable {

    private long   id   = 0;
    private String name = null;

    public PersonImmutable(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

}

To tell Jackson that it should call the constructor of PersonImmutable we must add the @JsonCreator annotation to the constructor. But that alone is not enough. We must also annotate the parameters of the constructor to tell Jackson which fields from the JSON object to pass to which constructor parameters. Here is how the PersonImmutable class looks with the @JsonCreator and @JsonProperty annotations added:

public class PersonImmutable {

    private long   id   = 0;
    private String name = null;

    @JsonCreator
    public PersonImmutable(
            @JsonProperty("id")  long id,
            @JsonProperty("name") String name  ) {

        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

}

Notice the annotation above the constructor and the annotations before the constructor parameters. Now Jackson is capable of creating a PersonImmutable from this JSON object:

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

@JacksonInject

The Jackson annotation @JacksonInject is used to inject values into the parsed objects, instead of reading those values from the JSON. For instance, imagine you are downloading person JSON objects from various different sources, and would like to know what source a given person object came from. The sources may not themselves contain that information, but you can have Jackson inject that into the Java objects created from the JSON objects.

To mark a field in a Java class as a field that needs to have its value injected by Jackson, add the @JacksonInject annotation above the field. Here is an example PersonInject class with the @JacksonInject annotation added above the source field:

public class PersonInject {

    public long   id   = 0;
    public String name = null;

    @JacksonInject
    public String source = null;

}

In order to have Jackson inject values into the source field you need to do a little extra when creating the Jackson ObjectMapper. Here is what it takes to have Jackson inject values into the Java objects:

InjectableValues inject = new InjectableValues.Std().addValue(String.class, "jenkov.com");
PersonInject personInject = new ObjectMapper().reader(inject)
                        .forType(PersonInject.class)
                        .readValue(new File("data/person.json"));

Notice how the value to inject into the source attribute is set in the InjectableValues addValue() method. Notice also that the value is only tied to the type String - not to any specific field name. It is the @JacksonInject annotation that specifies what field the value is to be injected into.

If you were to download person JSON objects from multiple sources and have a different source value injected for each source, you would have to repeat the above code for each source.

@JsonDeserialize

The Jackson annotation @JsonDeserialize is used to specify a custom de-serializer class for a given field in a Java object. For instance, imagine you wanted to optimize the on-the-wire formatting of the boolean values false and true to 0 and 1.

First you would need to add the @JsonDeserialize annotation to the field you want to use the custom deserializer for. Here is how adding the @JsonDeserialize annotation to a field looks like:

public class PersonDeserialize {

    public long    id      = 0;
    public String  name    = null;

    @JsonDeserialize(using = OptimizedBooleanDeserializer.class)
    public boolean enabled = false;
}

Second, here is what the OptimizedBooleanDeserializer class referenced from the @JsonDeserialize annotation looks like:

public class OptimizedBooleanDeserializer
    extends JsonDeserializer<Boolean> {

    @Override
    public Boolean deserialize(JsonParser jsonParser,
            DeserializationContext deserializationContext) throws
        IOException, JsonProcessingException {

        String text = jsonParser.getText();
        if("0".equals(text)) return false;
        return true;
    }
}

Notice that the OptimizedBooleanDeserializer class extends JsonDeserializer with the generic type Boolean . Doing so makes the deserialize() method return a Boolean object. If you were to deserialize another type (e.g a java.util.Date) you would have to specify that type inside the generics brackets (if you are new to Java generics, I have a Java Generics Tutorial too).

You obtain the value of the field to deserialize by calling the getText() method of the jsonParser parameter. You can then deserialize that text into whatever value and type your deserializer is targeted at (a Boolean in this example).

Finally you need to see what it looks like to deserialize an object with a custom deserializer and the @JsonDeserializer annotation:

PersonDeserialize person = objectMapper
        .reader(PersonDeserialize.class)
        .readValue(new File("data/person-optimized-boolean.json"));

Notice how we first need to create a reader for the PersonDeserialize class using the ObjectMapper's reader() method, and then we call readValue() on the object returned by that method.

Write Annotations

Jackson also contains a set of annotations that can influence how Jackson serializes (writes) Java objects to JSON. Each of these write (serialization) annotations will be covered in the following sections.

@JsonInclude

The Jackson annotation @JsonInclude tells Jackson only to include properties under certain circumstances. For instance, that properties should only be included if they are non-null, non-empty, or have non-default values. Here is an example that shows how you can use the @JsonInclude annotation:

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonInclude {

    public long  personId = 0;
    public String name     = null;

}

This example will only include the name property if the value set for it is non-empty, meaning is not null and is not an empty string.

A more saying name for the @JsonInclude annotation could have been @JsonIncludeOnlyWhen, but that would have been longer to write.

@JsonGetter

The @JsonGetter Jackson annotation is used to tell Jackson that a certain field value should be obtained from calling a getter method instead of via direct field access. The @JsonGetter annotation is useful if your Java class uses jQuery style for getter and setter names. For instance, instead of getPersonId() and setPersonId() you might have the methods personId() and personId(long id).

Here is an example class named PersonGetter which shows the use of the @JsonGetter annotation:

public class PersonGetter {

    private long  personId = 0;

    @JsonGetter("id")
    public long personId() { return this.personId; }

    @JsonSetter("id")
    public void personId(long personId) { this.personId = personId; }

}

As you can see, the personId() method is annotated with the @JsonGetter annotation. The value set on the @JsonGetter annotation is the name that should be used in the JSON object. Thus, the name used for the personId in the JSON object is id. The resulting JSON object would look like this:

{"id":0}

Notice also that the personId(long personId) method is annotated with the @JsonSetter annotation to make Jackson recognized that as the setter matching the id attribute in the JSON object. The @JsonSetter annotation is used when reading from JSON into Java objects - not when writing Java objects to JSON. The @JsonSetter annotation is just included for the sake of completeness.

@JsonAnyGetter

The @JsonAnyGetter Jackson annotation enables you to use a Map as container for properties that you want to serialize to JSON. Here is an example of using the @JsonAnyGetter annotation in a Java class:

public class PersonAnyGetter {

    private Map<String, Object> properties = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> properties() {
        return properties;
    }
}

When seeing the @JsonAnyGetter annotation Jackson will obtain the Map returned from the method which @JsonAnyGetter annotates, and will treat each key-value pair in that Map as a property. In other words, all key-value pairs in the Map will be serialized to JSON as part of the PersonAnyGetter object.

@JsonPropertyOrder

The @JsonPropertyOrder Jackson annotation can be used to specify in what order the fields of your Java object should be serialized into JSON. Here is an example showing how to use the @JsonPropertyOrder annotation:

@JsonPropertyOrder({"name", "personId"})
public class PersonPropertyOrder {

    public long  personId  = 0;
    public String name     = null;

}

Normally Jackson would have serialized the properties in PersonPropertyOrder in the sequence they are found in the class. However, the @JsonPropertyOrder annotation specifies a different order where the name property will be appear first and the personId property second in the serialized JSON output.

@JsonRawValue

The @JsonRawValue Jackson annotation tells Jackson that this property value should written directly as it is to the JSON output. If the property is a String Jackson would normally have enclosed the value in quotation marks, but if annotated with the @JsonRawValue property Jackson won't do that.

To make it more clear what @JsonRawValue does, look at this class without the @JsonRawValue in use:

public class PersonRawValue {

    public long   personId = 0;

    public String address  = "$#";
}

Jackson would serialize this into this JSON string:

{"personId":0,"address":"$#"}

Now we add the @JsonRawValue to the address property, like this:

public class PersonRawValue {

    public long   personId = 0;

    @JsonRawValue
    public String address  = "$#";
}

Jackson will now omit the quotation marks when serializing the address property. The serialized JSON will thus look like this:

{"personId":0,"address":$#}

This is of course invalid JSON, so why would you want that?

Well, if the address property contained a JSON string then that JSON string would be serialized into the final JSON object as part of the JSON object structure, and not just into a string in the address field in the JSON object. To see how that would work, let us change the value of the address property like this:

public class PersonRawValue {

    public long   personId = 0;

    @JsonRawValue
    public String address  =
            "{ \"street\" : \"Wall Street\", \"no\":1}";

}

Jackson would serialize this into this JSON:

{"personId":0,"address":{ "street" : "Wall Street", "no":1}}

Notice how the JSON string is now part of the serialized JSON structure.

Without the @JsonRawValue annotation Jackson would have serialized the object to this JSON:

{"personId":0,"address":"{ \"street\" : \"Wall Street\", \"no\":1}"}

Notice how the value of the address property is now enclosed in quotation marks, and all the quotation marks inside the value are escaped.

@JsonValue

The Jackson annotation @JsonValue tells Jackson that Jackson should not attempt to serialize the object itself, but rather call a method on the object which serializes the object to a JSON string. Note that Jackson will escape any quotation marks inside the String returned by the custom serialization, so you cannot return e.g. a full JSON object. For that you should use @JsonRawValue instead (see previous section).

The @JsonValue annotation is added to the method that Jackson is to call to serialize the object into a JSON string. Here is an example showing how to use the @JsonValue annotation:

public class PersonValue {

    public long   personId = 0;
    public String name = null;

    @JsonValue
    public String toJson(){
        return this.personId + "," + this.name;
    }

}

The output you would get from asking Jackson to serialize a PersonValue object is this:

"0,null"

The quotation marks are added by Jackson. Remember, any quotation marks in the value string returned by the object are escaped.

@JsonSerialize

The @JsonSerialize Jackson annotation is used to specify a custom serializer for a field in a Java object. Here is an example Java class that uses the @JsonSerialize annotation:

public class PersonSerializer {

    public long   personId = 0;
    public String name     = "John";

    @JsonSerialize(using = OptimizedBooleanSerializer.class)
    public boolean enabled = false;
}

Notice the @JsonSerialize annotation above the enabled field.

The OptimizedBooleanSerializer will serialize a true value to 1 and a false value 0. Here is the code:

public class OptimizedBooleanSerializer extends JsonSerializer<Boolean> {

    @Override
    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, 
        SerializerProvider serializerProvider) 
    throws IOException, JsonProcessingException {

        if(aBoolean){
            jsonGenerator.writeNumber(1);
        } else {
            jsonGenerator.writeNumber(0);
        }
    }
}

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC