Jackson

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.

Download
You can download the full source code of this example here: Jackson Annotation Examples

Anmol Deep

Anmol Deep is a senior engineer currently working with a leading identity security company as a Web Developer. He has 8 years of programming experience in Java and related technologies (including functional programming and lambdas) , Python, SpringBoot, Restful architectures, shell scripts, and databases relational(MySQL, H2) and nosql solutions (OrientDB and MongoDB). He is passionate about researching all aspects of software development including technology, design patterns, automation, best practices, methodologies and tools, and love traveling and photography when not coding.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button