Jackson

Jackson Exceptions – Problems and Solutions

1. Introduction

Jackson Exceptions

In this example, we shall go through the most common Jackson API exceptions encountered while working for serialization and deserialization. We will see what caused the exception to be thrown and how to fix it. Let’s dive deep.

 

2. “InvalidDefinitionException: No Creators, like default constructor, exist”

2.1. Model

Consider the following model class Shop for deserialization operations for this example.

Shop.java

public class Shop {

	public int id;
	public String name;

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

2.2. Exception

The below code attempts to deserialize a JSON string to an object of class Shop. The code when executed results in an exception at runtime.

Deserialization

	private static void jacksonNoConstructor() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"id\":1,\"name\":\"John\"}";
		try {
			mapper.readValue(json, Shop.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

An InvalidDefinitionException is thrown. The exception and the stack trace is:

Stack Trace

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `jackson.exceptions.Shop` 
(no Creators, like default constructor, exist):
cannot deserialize from Object value 
(no delegate- or property-based Creator)
at [Source: .[Source: (String undefined)"{"id":1,"name":"John"}";
line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(
InvalidDefinitionException.java:67 undefined)

2.3. Problem

Jackson API during deserialization is unable to identify a suitable constructor to create an instance of the class Shop, which doesn’t define a no-argument default constructor.

2.4. Solution

Provide a default constructor in the Shop class as shown below to fix this error.

Shop.java [with fix]

public class Shop {

	public int id;
	public String name;

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

	public Shop() {
	}
}

The deserialization after applying the fix will be successful without any exception at runtime.

3. MismatchedInputException: Out of START_ARRAY token

3.1. Model

Consider the following model class Customer with two fields id and name.

Customer.java

public class Customer {

	public int id;
	public String name;
}

3.2. Exception

The below code attempts to deserialize an array of JSON objects of type Customer to an instance of the Customer class. This code when executed throws an exception at runtime.

Deserialization

	private static void jacksonListToObject() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "[{\"id\":1,\"name\":\"John\"},{\"id\":2,\"name\":\"Adam\"}]";
		try {
			mapper.readValue(json, Customer.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

A MismatchedInputException is thrown. The exception and the stack trace is:

Stack Trace

com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of
`jackson.exceptions.Customer` out of START_ARRAY token
at [Source: (String)"[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]";
line: 1, column: 1] at com.fasterxml.jackson.databind.exc.
MismatchedInputException.from(MismatchedInputException.java:59)

3.3. Problem

We are trying to deserialize a JSON array to an object which is not of the type List but Customer. The above exception is thrown when we use the wrong type during deserialization.

3.4. Solution

The fix is to deserialize the JSON array to an object of type List<Customer> instead of type Customer. The below code demonstrates this.

Deserialization [With Fix]

	private static void jacksonListToObjectFix() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "[{\"id\":1,\"name\":\"John\"},"
				+ "{\"id\":2,\"name\":\"Adam\"}]";
		try {
			List<Customer> customer = mapper.readValue
					(json, new TypeReference<List>() {
			});
			System.out.println(customer);
			// Prints [Customer [id=1, name=John], 
			// Customer [id=2, name=Adam]]
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + " : " 
					+ e.getOriginalMessage());
		}
	}

The deserialization after applying the fix will convert the JSON array to a List<Customer> without throwing any exception at runtime.

4. InvalidDefinitionException: No properties discovered to create BeanSerializer

4.1. Model

Consider the following model class Product.

Product.java

public class Product {

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

4.2. Exception

The following code attempts to serialize an instance of the class Product. The code executes and errors out with an exception at runtime.

Serialization

	private static void privateFieldAndGetter() {
		ObjectMapper mapper = new ObjectMapper();
		try {
			Product p = new Product(1, "Anmol");
			mapper.writeValueAsString(p);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

An InvalidDefinitionException is thrown. The exception and the stack trace is:

Stack Trace

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class jackson.exceptions.Product and no
properties discovered to create BeanSerializer (to avoid exception,
disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from
(InvalidDefinitionException.java:77)

4.3. Problem

The Jackson API is unable to auto-detect public getters or public fields available in the model class for serialization. In other words, our model class does not expose any public getters or public fields.

4.4. Solution

There are multiple solutions to fix this problem. Let’s discuss each of them with examples.

4.4.1. Modify The Model Class

This is the easiest solution and we may use it only if we have an option to modify the model class and add public getters for all fields.

[Fix] Add Public Getters

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

4.4.2. ObjectMapper setVisibility method

We may also choose to configure the ObjectMapper by invoking the setVisibility method. Setting the visibility of FIELD to ANY will fix this problem as shown below.

[Fix] ObjectMapper setVisibility

	private static void privateFieldAndGetterFix() {
		ObjectMapper mapper = new ObjectMapper();
		try {
			Product p = new Product(1, "Anmol");
			mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
			String json = mapper.writeValueAsString(p);
			System.out.println(json);
			// Prints {"id":1,"name":"Anmol"}
		} catch (JsonProcessingException e) {
		}
	}

4.4.3. @JsonAutoDetect annotation

We can also use the @JsonAutoDetect annotation to fix this problem. For a detailed understanding of this annotation, check out the example on Jackson Annotations.

[Fix] @JsonAutoDetect

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

5. InvalidFormatException: Cannot deserialize value of type `int` from String

5.1. Model

Consider the following model class Customer.

Customer.java

public class Customer {

	public int id;
	public String name;
}

5.2. Exception

The below code attempts to deserialize a JSON string to an object of class Customer. The code when executed results in an exception at runtime.

Deserialization

	private static void cannotDeserialize() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"id\":\"Three\",\"name\":\"Anmol\"}";
		try {
			mapper.readValue(json, Customer.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

5.3. Problem

Clearly we are trying to feed in value of an incompatible type String from the JSON to an integer field in our model class. This will not work as the text value “Three” is not a number(integer). Therefore, an InvalidFormatException is thrown.

5.4. Solution

In such cases, there are two extreme solutions.

One is to change this field’s type in the model class from int to String to make the JSON and model class compatible. The other solution is for the producer of the JSON string to serialize the value of the property in the correct type. i.e. int in this case.

6. UnrecognizedPropertyException: Unrecognized field { }

6.1. Model

Consider the following model class Customer for this example.

Customer.java

public class Customer {

	public int id;
	public String name;
}

6.2. Exception

The following code attempts to deserialize a JSON string and throws an exception.

Deserialization

	private static void unknownProperty() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"id\":99,\"name\":\"Anmol\",\"location\":\"Bangalore\"}";
		try {
			mapper.readValue(json, Customer.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() +
					" : " + e.getOriginalMessage());
		}
	}

6.3. Problem

This is a very common exception and is thrown when an unrecognized property is identified in the JSON string which is not available in the model class.

In the above code, the JSON string contains an additional field “location” unavailable in the class Customer causing the exception to be thrown.

6.4. Solution

This problem can be solved by using multiple approaches. Let’s discuss those.

6.4.1. The @JsonIgnore annotation

We can annotate our model class with @JsonIgnoreProperties and define a rule to ignore unknown properties. The below example shows this.

Customer.java [With Fix]

@JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {

	public int id;
	public String name;
}

6.4.2. The DeserializationFeature

We may also use the DeserializationFeature FAIL_ON_UNKNOWN_PROPERTIES and disable it on the ObjectMapper. This will cause deserialization not to fail on detecting unknown properties.

Deserialization [With Fix]

	private static void unknownPropertyFix() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"id\":99,\"name\":\"Anmol\",\"location\":\"Bangalore\"}";
	    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		try {
			mapper.readValue(json, Customer.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() +
					" : " + e.getOriginalMessage());
			e.printStackTrace();
		}
	}

The deserialization after applying the fix will be successful without any exception at runtime.

7. MismatchedInputException: Root name { } does not match expected

7.1. Model

Consider the following model class Customer for this example.

Customer.java

public class Customer {

	public int id;
	public String name;
}

7.2. Exception

The below code attempts to deserialize a JSON string to an object of class Customer. The code when executed results in an exception at runtime.

Deserialization

	private static void wrongJsonRoot() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"jsonRoot\" : {\"id\":1,\"name\":\"John\"}}";
		try {
			mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
			mapper.readValue(json, Customer.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

A MismatchedInputException is thrown. The exception and the stack trace is:

Stack Trace

com.fasterxml.jackson.databind.exc.MismatchedInputException:
Root name 'jsonRoot' does not match expected ('Customer')
for type [simple type, class jackson.exceptions.Customer]
at [Source: (String)"{"jsonRoot" : {"id":1,"name":"John"}}";
line: 1, column: 2] (through reference chain: jackson.
		exceptions.Customer["jsonRoot"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.
from(MismatchedInputException.java:63)

7.3. Problem

In the above code, the UNWRAP_ROOT_VAUE deserialization feature has been enabled on the ObjectMapper. This feature will unwrap the root-level JSON value while deserialization.

However, the Jackson API is unable to locate in the model class, the root name provided in the JSON string. This causes a MismatchedInputException to be thrown.

7.4. Solution

To fix this problem, we need to annotate our model class with the annotation @JsonRootName and define the name to be used for root-level wrapping. This is shown in the example below.

Customer.java [With Fix]

@JsonRootName("jsonRoot")
public class Customer {

	public int id;
	public String name;
}

Deserialization After Fix

	private static void wrongJsonRootFix() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{\"jsonRoot\" : {\"id\":1,\"name\":\"John\"}}";
		try {
			mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
			Customer customer = mapper.readValue(json, Customer.class);
			System.out.println(customer.name + " - " + customer.id);
			// Print John - 1
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

The above deserialization code will now execute without throwing any exception.

8. JsonParseException: Unexpected character (code 39)

8.1. Model

Consider the following model class Employee for this example.

Employee.java

public class Employee {

	public int id;
	public String name;
}

8.2. Exception

The following code attempts to deserialize a JSON string to an instance of class Employee and errors out with an exception.

Deserialization

	private static void code39Exception() {
		ObjectMapper mapper = new ObjectMapper();
		String json = "{'id':99,'name':'Anmol'}";
		try {
			mapper.readValue(json, Employee.class);
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() + 
					" : " + e.getOriginalMessage());
		}
	}

A JsonParseException is thrown. The exception and stack trace is:

Stack Trace

com.fasterxml.jackson.core.JsonParseException: Unexpected character (''' 
(code 39)): was expecting double-quote to start field name
at [Source: (String)"{'id':99,'name':'Anmol'}"; line: 1, column: 3]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1851)

8.3. Problem

If we carefully observe the deserialization code in the section above, the JSON string is composed of single-quotes instead of double-quotes. Therefore, deserializing a JSON string with single-quotes results in a code 39 error.

8.4. Solution

The ObjectMapper class provides a constructor to which we can supply our own JsonFactory with some features enabled. The fix for this problem is also around the same lines. We can configure the ObjectMapper to allow single quotes as demonstrated in the below example.

[Fix] A JsonFactory With Single Quotes enabled, supplied to ObjectMapper

	private static void code39Fix() {
		String json = "{'id':99,'name':'Anmol'}";
		try {
			JsonFactory jf = new JsonFactory();
			jf.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
			ObjectMapper mapper = new ObjectMapper(jf);
			Employee employee = mapper.readValue(json, Employee.class);
			System.out.println(employee.id + " = " + employee.name);
			// Prints 99 = Anmol
		} catch (JsonProcessingException e) {
			System.out.println(e.getClass().getName() +
					" : " + e.getOriginalMessage());
		}
	}

9. Summary

In this article, we spoke about Jackson Exceptions. Specifically, we explored some common exceptions that are encountered in programming with the Jackson API for serialization/deserialization. We did a deep dive into the causes of such exceptions and looked at the way to fix them.

10. Download the source code

Download
You can download the full source code of this example here: Jackson Exceptions – Problems and Solutions

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
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button