Jackson Annotation Examples
This example is a deep dive into Jackson Annotations. It covers almost all the annotations with code examples. We will learn how to use the existing annotations, create our custom annotations and finally how to completely disable Jackson annotations.
If you are new to the Jackson API for JSON serialization and deserialization, check out this article on Introduction to Jackson ObjectMapper before proceeding further in this example.
1. Jackson Annotations Set – I
This section deals with some Jackson annotations that are quite easy to use and simple to understand.
1.1. @JsonIgnore
The @JsonIgnore
annotation is used at the field level, to mark a property to be ignored from being serialized. For instance, the field category will be ignored from serialization.
@JsonIgnore
@JsonIgnore private String category;
1.2. @JsonIgnoreProperties
@JsonIgnoreProperties
annotation can be used at the class level to mark a single property or a list of properties to be ignored from serialization. The below example will ignore fields ignoreField1 and ignoreField2 from getting serialized.
@JsonIgnoreProperties
@JsonIgnoreProperties({ "ignoreField1", "ignoreField2" }) public class Animal {
1.3. @JsonIgnoreType
Use @JsonIgnoreType
annotation to mark and ignore all the properties of an annotated type for serialization and deserialization operations. In the below example, we have annotated an inner class named Sound with the annotation @JsonIgnoreType
.
@JsonIgnoreType
@JsonIgnoreType class Sound {
1.4. @JsonInclude
We can specify certain rules with the @JsonInclude
annotation to exclude or reduce the number of properties for serialization. For instance, you might want to only include properties for serialization that have non-null or non-empty values. The below example shows this.
@JsonInclude
@JsonInclude(Include.NON_NULL) public class Animal {
1.5. @JsonPropertyOrder
Use the annotation @JsonPropertyOrder
to define an order in which the properties appear in the JSON after serialization. The properties missing in the order definition appear after the properties included in the definition. The following example shows this.
@JsonPropertyOrder
@JsonPropertyOrder({ "name", "code", "jsonPropertyId", "now", "feed", "seaAnimal" }) public class Animal {
1.6. @JsonProperty
The @JsonProperty
annotation can be used at a field level to specify a property name to be used in the JSON that is different from the field name in the corresponding Java class.
However, when used at a method level, it marks a non-standard getter/setter to be used for serializing/deserializing a JSON property. Let’s see an example.
@JsonProperty
@JsonProperty("jsonPropertyId") private int id; ------------------------------------------------ public class Animal { private String name; @JsonProperty("name") public void nameSetterMethod(String name) { this.name = name; } }
1.7. @JsonGetter
We can use the @JsonGetter
annotation as an alternative to the recommended @JsonProperty
annotation. This annotation marks a non-static no-argument method that returns a non-void value, as a “getter” for a property. See the below example.
@JsonGetter
@JsonGetter("name") public String anotherGetNameMethod() { return name; }
1.8. @JsonSetter
Just like the @JsonGetter, the @JsonSetter
annotation marks a non-static method, one-argument method as a “setter” for a property.
During deserialization, this method will set the value of the property when it is encountered in the JSON with a name matching the one defined in the annotation.
@JsonSetter
@JsonSetter("code") public void anotherSetterForCode(long code) { this.code = code; }
1.9. @JsonRawValue
The @JsonRawValue
annotation can be used to command Jackson to include the literal String value of the property just as it is, during serialization. In the below example, the literal String value of the JSON string will be available in the output JSON.
@JsonRawValue
@JsonRawValue public String feed; ............ // constructor code this.feed = "{\"stockAvailable\":true,\"type\":\"@JsonRawValue\"}";
1.10. @JsonRootName
This annotation provides a name for root-level wrapping of the JSON string. The deserializer will look for the same name as well. Enable wrapping on the ObjectMapper to use this annotation as shown below.
@JsonRootName
@JsonRootName(value = "javaCodeGeeks") public class Animal { .................................... // main method mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // After Serialization, the following JSON is generated { "javaCodeGeeks" : { "name" : "Dog", ................ } }
1.11. @JsonPropertyDescription
The Jackson API provides a capability to generate a JSON schema. This annotation defines and populates a description field in such a schema. The following example demonstrates this.
@JsonPropertyDescription
public class AnimalSchema { @JsonPropertyDescription("This is a description of the animal property") private Animal animal; // getters and setters } .............................. // main method SchemaFactoryWrapper schemaFactoryWrapper = new SchemaFactoryWrapper(); mapper.acceptJsonFormatVisitor(AnimalSchema.class, schemaFactoryWrapper); JsonSchema jsonSchema = schemaFactoryWrapper.finalSchema(); String jsonSchemaString = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(jsonSchema);
1.12. @JsonFormat
The @JsonFormat
annotation specifies a format for serializing Date-Time values as shown in the following example.
@JsonFormat
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MMM-dd-MM-yyyy hh:mm:ss Z") private Date now;
1.13. @JsonAutoDetect
Jackson Auto-detection is a capability that uses name conventions and/or method signature to find methods which are used for data-binding.
The @JsonAutoDetect
annotation can be used to configure the visibility of fields, methods, getters, and creators to ANY(all access modifiers acceptable), NON_PRIVATE, PROTECTED_AND_PUBLIC, PUBLIC_ONLY and NONE(no access modifiers are auto-detectable).
@JsonAutoDetect
@JsonAutoDetect(fieldVisibility = Visibility.ANY) public class Animal { .. // no getter/setter private boolean seaAnimal;
The above example sets the field visibility AutoDetection to ANY(any access modifier) and the private field seaAnimal without any getter/setter method still will be available for auto-detection.
1.14. @JsonCreator
The @JsonCreator
annotation can be used to annotate constructors to instantiate new instances of the associated class on deserialization. Additionally, the @JsonProperty
annotation can be used to specify the properties in the JSON which aren’t present in the target class. Let’s see with an example.
@JsonCreator
@JsonCreator public Animal(@JsonProperty("deserId") int id, @JsonProperty("deserName") String name) { this.id = id; this.name = name; } .............................. // main class String json = "{\"deserId\":1000,\"deserName\":\"My Animal\"}"; Animal animal = new ObjectMapper().readValue(json, Animal.class);
As you can see above, the properties deserId & deserName do no exist in the target POJO. Hence, instead of modifying the POJO, we can use @JsonCreator
& @JsonProperty
together to define a constructor that can be used to deserialize such JSON strings.
The following example shows in action all the annotations we discussed above.
Set 1 Example
package annotationSetOne; import java.util.Date; import com.fasterxml.jackson.annotation.* @JsonAutoDetect(fieldVisibility = Visibility.ANY) @JsonIgnoreProperties({ "ignoreField1", "ignoreField2" }) @JsonInclude(Include.NON_NULL) @JsonPropertyOrder({ "name", "code", "jsonPropertyId", "now", "feed", "seaAnimal" }) @JsonRootName(value = "javaCodeGeeks") public class Animal { @JsonProperty("jsonPropertyId") private int id; private String name; private long code; private boolean seaAnimal; private Sound sound; private String origin; @JsonRawValue public String feed; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MMM-dd-MM-yyyy hh:mm:ss Z") private Date now; @JsonIgnore private String category; @JsonIgnoreType class Sound { private String name; private boolean recording; Sound(String name, boolean recording) { this.name = name; this.recording = recording; } // standard getters and setters } private String ignoreField1; private String ignoreField2; public Animal(int id, String name, long code, boolean seaAnimal, Date now) { this.id = id; this.name = name; this.code = code; this.seaAnimal = seaAnimal; this.now = now; this.category = "@JSONIGNORE"; this.ignoreField1 = "@JSONIGNOREPROPERTIES"; this.ignoreField2 = "@JSONIGNOREPROPERTIES"; this.sound = new Sound(name + "-sound", true); this.feed = "{\"stockAvailable\":true,\"type\":\"@JsonRawValue\"}"; } @JsonCreator public Animal(@JsonProperty("deserId") int id, @JsonProperty("deserName") String name) { this.id = id; this.name = name; } @JsonGetter("name") public String anotherGetNameMethod() { return name; } @JsonSetter("code") public void anotherSetterForCode(long code) { this.code = code; } public void setName(String name) { this.name = name; } public long getCode() { return code; } // standard getters and setters }
AnimalSchema.java
public class AnimalSchema { @JsonPropertyDescription("This is a description of the animal property") private Animal animal; public Animal getAnimal() { return animal; } public void setAnimal(Animal animal) { this.animal = animal; } }
Runner.java
public static void main(String[] args) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); Animal animal = new Animal(1, "Dog", 121212122323323L, false, new Date()); mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); System.out.println(mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(animal)); String json = "{\"deserId\":1000,\"deserName\":\"My Animal\"}"; Animal animal2 = mapper.readValue(json, Animal.class); System.out.println(animal2.getId() + " - " + animal2.anotherGetNameMethod()); SchemaFactoryWrapper schemaFactoryWrapper = new SchemaFactoryWrapper(); mapper.acceptJsonFormatVisitor(AnimalSchema.class, schemaFactoryWrapper); JsonSchema jsonSchema = schemaFactoryWrapper.finalSchema(); String jsonSchemaString = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(jsonSchema); System.out.println(jsonSchemaString); }
Program Output
{ "javaCodeGeeks" : { "name" : "Dog", "code" : 121212122323323, "jsonPropertyId" : 1, "now" : "Jun-18-06-2020 09:32:54 +0000", "feed" : {"stockAvailable":true,"type":"@JsonRawValue"}, "seaAnimal" : false } } 1000 - My Animal { "ObjectSchema" : { "type" : "object", "id" : "urn:jsonschema:annotationSetOne:AnimalSchema2", "properties" : { "animal" : { "type" : "object", "id" : "urn:jsonschema:annotationSetOne:Animal", "description" : "This is a description of the animal property", "properties" : { "name" : { "type" : "string" }, "code" : { "type" : "integer" }, "jsonPropertyId" : { "type" : "integer" }, "now" : { "type" : "string", "format" : "date-time" }, "feed" : { "type" : "string" }, "seaAnimal" : { "type" : "boolean" }, "origin" : { "type" : "string" } } } } } }
Eclipse Version: 2019-06 (4.12.0);
Java version: Java 11;
pom.xml dependencies:
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-jsonSchema</artifactId> <version>2.11.0</version> </dependency> </dependencies>
2. Jackson Annotations Set – II
In this section, we will go through a few more annotations and explain their usage with some code in action.
2.1. @JsonView
This annotation is used for annotating fields with a specific View indicating that the field is a part of that View. An ObjectMapper configured to use an ObjectWriter with that View serializes only the properties annotated for that View(or its subclass).
In addition, fields can be annotated for multiple View classes. Let us demonstrate this with an example. Below are defined two views (PublicView and CorporateView).
Views.java
public class Views { public static class PublicView { } public static class CorporateView extends PublicView { } }
Product.java
public class Product { @JsonView(Views.CorporateView.class) public int productId; @JsonView(Views.PublicView.class) public String productName; @JsonView(Views.PublicView.class) public String brandName; public Product(int productId, String productName, String brandName) { this.productName = productName; this.productName = productName; this.brandName = brandName; } } .......................................... // Runner.java public class Runner { public static void main(String[] args) throws JsonProcessingException { Product product = new Product(18765432, "Television", "LG"); String json = new ObjectMapper().writerWithView( Views.PublicView.class).writeValueAsString(product); System.out.println(json); // {"productName":"Television","brandName":"LG"} json = new ObjectMapper().writerWithView( Views.CorporateView.class).writeValueAsString(product); System.out.println(json); // {"productId":18765432,"productName":"Television","brandName":"LG"} } }
As you can clearly see, an ObjectWriter with view PublicView outputs only the fields annotated with the PublicView class and likewise for the CorporateView.
2.2. @JsonFilter
This annotation is used to mark a class as a “filter” for filtering out its properties.
In the below example, the class JsonFilterExample is annotated with @JsonFilter
and later this filter is used to filter out all properties except the property name.
JsonFilterExample.java
@JsonFilter("jcgFilter") public class JsonFilterExample { public int id; public String name; public JsonFilterExample(int id, String name) { this.id = id; this.name = name; } } ............................... // main method JsonFilterExample jfe = new JsonFilterExample(1, "Java Code Geeks JSON Filter"); FilterProvider filters = new SimpleFilterProvider().addFilter("jcgFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name")); System.out.println(new ObjectMapper().writer(filters).writeValueAsString(jfe)); // Ouput : {"name":"Java Code Geeks JSON Filter"}
2.3. @JsonValue
The annotation @JsonValue
is used to mark a no-args,non-void returning getter method to be used as the only value getting method for serialization.
An exception is thrown if more than one method is annotated with @JsonValue
or if the method signature is incompatible with the getter.
JsonFilterExample.java
public enum JsonValueExampleEnum { ONE(1, "One"), TWO(2, "Two"); private Integer id; private String name; private JsonValueExampleEnum(Integer id, String name) { this.id = id; this.name = name; } @JsonValue public String getName() { return name; } } .............................................. // main method String enumAsString = objectMapper.writeValueAsString(JsonValueExampleEnum.ONE); System.out.println(enumAsString); // Output: "One"
2.4. @JsonUnWrapped
The annotation @JsonUnWrapped
is used to specify that a property of a type must be unwrapped when serialized i.e. the properties of such a type should be included in the containing object in the JSON.
UnwrappedUser.java
public class UnwrappedUser { @JsonUnwrapped public Department dept; public UnwrappedUser(Department dept) { this.dept = dept; } public static class Department { public String name; public int id; public Department(String name, int id) { this.name = name; this.id = id; } } } ...................................... // main method JsonUnwrappedExample jue = new JsonUnwrappedExample( new JsonUnwrappedExample.Department("Supply Chain", 12345)); String result = objectMapper.writerWithDefaultPrettyPrinter() .writeValueAsString(jue); System.out.println(result); ..... // without @JsonUnWrapped { "dept" : { "name" : "Supply Chain", "id" : 12345 } } // with @JsonUnWrapped { "name" : "Supply Chain", "id" : 12345 }
2.5. @JacksonInject
The annotation @JacksonInject
is used to indicate that the value of the annotated property shall be injected and not deserialized from the JSON.
JacksonInjectExample.java
public class JacksonInjectExample { @JacksonInject public int id; public String name; // getters and setters } ................................... // main method String json = "{\"name\":\"Injectable\"}"; InjectableValues inject = new InjectableValues.Std() .addValue(int.class, 12345); JacksonInjectExample jie = new ObjectMapper() .reader(inject).forType(JacksonInjectExample.class) .readValue(json); System.out.println(jie); // JacksonInjectExample [id=12345, name=Injectable]
2.6. @JsonAnySetter
This annotation defines a non-static, two-argument method to be used as a “fallback” handler for all unrecognized properties found in the JSON. A Map, for instance, can be used for a @JsonAnySetter
to consume all unrecognized properties found in the JSON string that get added to the Map.
AnyGetterSetterExample.java
@JsonAnySetter public void add(String property, String value) { properties.put(property, value); }
2.7. @JsonAnyGetter
Just like @JsonAnySetter
, this annotation defines a non-static, no-argument method as a getter for serializing the properties returned as a map by this getter.
AnyGetterSetterExample.java
public class AnyGetterSetterExample { private Map properties; public AnyGetterSetterExample() { properties = new HashMap(); } @JsonAnyGetter public Map getProperties() { return properties; } @JsonAnySetter public void add(String property, String value) { properties.put(property, value); } } .................................... // main method AnyGetterSetterExample example = new AnyGetterSetterExample(); example.add("SampleKey1", "SampleValue1"); example.add("SampleKey2", "SampleValue2"); String jsonString = objectMapper.writerWithDefaultPrettyPrinter() .writeValueAsString(example); System.out.println(jsonString); // without @AnyGetter { "properties" : { "SampleKey1" : "SampleValue1", "SampleKey2" : "SampleValue2" } } // With @AnyGetter { "SampleKey1" : "SampleValue1", "SampleKey2" : "SampleValue2" } .................................... String json = "{\"MapKey1\" : \"MapValue1\", \"MapKey2\" : \"MapValue2\"}"; AnyGetterSetterExample anyGetSetExample = objectMapper.readValue(json, AnyGetterSetterExample.class); System.out.println(anyGetSetExample.getProperties().get("MapKey1")); System.out.println(anyGetSetExample.getProperties().get("MapKey2"));
3. Jackson Polymorphic Type Handling Annotations
Let’s explore the Jackson polymorphic type handling annotations in this section.
3.1. @JsonTypeInfo
We can use this annotation to configure what type of information is used with JSON serialization and deserialization and how. The example in the next section will help this understand better.
3.2. @JsonTypeName
We can use this annotation along with @JsonTypeInfo
to give a logical name to the annotated class and bind it to the class.
3.3. @JsonSubTypes
We can use this annotation along with @JsonTypeInfo
to indicate the subtypes of the annotated type, along with their logical names.
JacksonSubTypesExample.java
public class JacksonSubTypesExample { @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Apple.class, name = "apple"), @JsonSubTypes.Type(value = Mango.class, name = "mango") }) static class Fruit { public String name; Fruit(String name) { this.name = name; } public Fruit() { } } @JsonTypeName("apple") static class Apple extends Fruit { public String size; Apple() { } Apple(String name, String size) { super(name); this.size = size; } } @JsonTypeName("mango") static class Mango extends Fruit { public int weight; Mango() { } Mango(String name, int weight) { super(name); this.weight = weight; } } }
Runner.java
Fruit mango = new JacksonSubTypesExample.Mango("CustomMango", 5); String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mango); System.out.println(result); String json = "{\"name\":\"CustomApple\",\"size\":\"big\", \"type\":\"apple\"}"; Fruit apple = objectMapper.readerFor(Apple.class).readValue(json); System.out.println(apple.name);
Program Output
{ "type" : "mango", "name" : "CustomMango", "weight" : 5 } CustomApple
Eclipse Version: 2019-06 (4.12.0);
Java version: Java 11;
pom.xml dependencies:
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-jsonSchema</artifactId> <version>2.11.0</version> </dependency> </dependencies>
4. Custom Annotations With @JacksonAnnotationsInside
We can use the meta-annotation @JacksonAnnotationsInside
to create a custom Jackson annotation. Such an annotation can be created by annotating it with @JacksonAnnotationsInside
and the other set of annotations this custom annotation is intended to combine. See the below example to understand how to create a custom Jackson annotation.
@Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonInclude(Include.NON_NULL) @JsonPropertyOrder({ "name", "id", "dateCreated" }) public @interface MyJacksonAnnotation { }
Using the annotation @MyJacksonAnnotation
on an entity will have the same effect as what would have been achieved by using the above Jackson annotations separately.
5. Disable Jackson Annotations
Lastly, let’s see how can we disable all the Jackson annotations. We can achieve this by disabling the MapperFeature.USE_ANNOTATIONS as shown in the below code snippet.
Disable Jackson Annotations
mapper.disable(MapperFeature.USE_ANNOTATIONS);
Once we disable the MapperFeature.USE_ANNOTATIONS, any Jackson Annotation used will have no effect and the Jackson API library will apply the default for any serialization/deserialization operation.
6. Conclusion
This example explained in depth the different Jackson annotations, their functionalities and the scenarios in which they should be used. Additionally, we saw how to create a custom Jackson annotation and how to disable all the Jackson annotations.
7. Download the source code
All the code examples provided in this tutorial examples are available in a Maven project and should be easy to import and run.
You can download the full source code of this example here: Jackson Annotation Examples