Deserialization of `@JsonTypeInfo` annotated type fails with missing type id even for explicit concrete subtypes
  • 14-May-2023
Lightrun Team
Author Lightrun Team
Share
Deserialization of `@JsonTypeInfo` annotated type fails with missing type id even for explicit concrete subtypes

Deserialization of `@JsonTypeInfo` annotated type fails with missing type id even for explicit concrete subtypes

Lightrun Team
Lightrun Team
14-May-2023

Explanation of the problem

When deserializing a concrete class that belongs to a polymorphic type hierarchy, an InvalidTypeIdException error is thrown if the JSON data does not contain the type id field. This issue can arise when using Jackson’s JsonTypeInfo and JsonSubTypes annotations on the interface and its implementing classes. The following example demonstrates the problem:

@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, property = “type”) @JsonSubTypes({ @JsonSubTypes.Type(value = Foo.class, name = “foo”), @JsonSubTypes.Type(value = Bar.class, name = “bar”)}) public interface Base {}

public class Foo implements Base {}

public class Bar implements Base {}

ObjectMapper mapper = new ObjectMapper();

mapper.readerFor(Foo.class).readValue(“{}”); // throws InvalidTypeIdException mapper.readValue(“{}”, Foo.class); // throws InvalidTypeIdException

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for: Deserialization of `@JsonTypeInfo` annotated type fails with missing type id even for explicit concrete subtypes

 

To resolve this issue, one workaround is to add the @JsonTypeInfo annotation to the concrete class and set its use property to JsonTypeInfo.Id.NONE. However, this approach has the downside of not serializing the type id property for that class. Another solution is to create a custom TypeIdResolver or SubTypeResolver. However, it may not be the most efficient or straightforward approach. A more intuitive solution would be for the mapper to fallback on checking if the class/type supplied to readerFor/readValue matches exactly one of the classes listed in JsonSubType if the type id property is not found.

In summary, when using Jackson’s JsonTypeInfo and JsonSubTypes annotations on a polymorphic type hierarchy, an InvalidTypeIdException error can occur if the JSON data does not contain the type id field. While workarounds such as setting the use property of the @JsonTypeInfo annotation to JsonTypeInfo.Id.NONE or creating custom TypeIdResolvers or SubTypeResolvers can solve the problem, a more intuitive solution would be for the mapper to fallback on checking the supplied class/type against the classes listed in JsonSubTypes.

 

Other popular problems with jackson-databind

 

Problem 1: Infinite loop during serialization or deserialization

One of the most common problems with Jackson Databind is an infinite loop that occurs during serialization or deserialization. This usually happens when there is a cyclic reference in the object graph being serialized or deserialized. Jackson tries to resolve these references, but sometimes it gets stuck in an infinite loop.

The solution to this problem is to use the @JsonIdentityInfo annotation to break the cycle. This annotation tells Jackson to use a unique identifier for each object instead of trying to serialize or deserialize the entire object graph. Here is an example:

public class Person { private String name; @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property=”id”) private List<Person> friends; // getters and setters }

In this example, the friends list contains a cyclic reference to other Person objects. The @JsonIdentityInfo annotation tells Jackson to use the “id” property to identify each Person object. When the friends list is serialized, Jackson will only include the ids of each Person object, rather than trying to serialize the entire object graph.

Problem 2: InvalidTypeIdException during deserialization

Another common problem with Jackson Databind is the InvalidTypeIdException that occurs during deserialization. This happens when Jackson is unable to determine the type of an object that is being deserialized. This can occur when the object being deserialized is part of a polymorphic type hierarchy and the JSON does not contain the type id field.

The solution to this problem is to use the @JsonTypeInfo annotation to include the type id field in the JSON. Here is an example:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = “type”) @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = “dog”), @JsonSubTypes.Type(value = Cat.class, name = “cat”) }) public abstract class Animal { private String name; // getters and setters }

In this example, the @JsonTypeInfo annotation tells Jackson to include a “type” field in the JSON that specifies the type of the Animal object being serialized or deserialized. The @JsonSubTypes annotation specifies the possible subtypes of Animal.

Problem 3: Performance issues with large JSON documents

Jackson Databind can also suffer from performance issues when dealing with large JSON documents. This is because Jackson has to parse the entire JSON document into memory before it can begin serialization or deserialization. For very large documents, this can cause out-of-memory errors or other performance problems.

The solution to this problem is to use streaming API provided by Jackson. The streaming API reads and writes JSON documents as a stream of tokens, rather than parsing the entire document into memory. This can greatly reduce memory usage and improve performance for large documents. Here is an example:

JsonFactory factory = new JsonFactory(); JsonParser parser = factory.createParser(new File(“example.json”)); while (parser.nextToken() != null) { // process each token here }

In this example, the JsonFactory creates a JsonParser that reads the JSON document as a stream of tokens. The while loop processes each token as it is read from the JSON document.

 

A brief introduction to jackson-databind

 

Jackson Databind is a Java library used to serialize and deserialize Java objects to and from JSON. It provides a simple and flexible API to work with JSON data, supporting features such as JSON Tree Model, Streaming API, and Data Binding. Jackson Databind is widely used in Java-based applications and has become the de facto standard for JSON processing in the Java ecosystem.

The library is built around two main classes, ObjectMapper and ObjectReader/ObjectWriter, which handle the serialization and deserialization of Java objects respectively. The ObjectMapper class provides methods to convert Java objects to JSON, and vice versa. The ObjectReader and ObjectWriter classes provide a fluent API to read and write JSON data, allowing for more fine-grained control over the serialization and deserialization process. Jackson Databind also supports annotations to customize the mapping of Java objects to JSON, and vice versa.

Overall, Jackson Databind is a powerful and flexible library for working with JSON in Java-based applications. Its ease of use and wide adoption in the Java ecosystem make it a popular choice for developers who need to work with JSON data.

 

Most popular use cases for jackson-databind

Here are three technical points about what jackson-databind can be used for:

  1. Parsing and Generating JSON data: jackson-databind can be used to parse JSON data into Java objects and to generate JSON data from Java objects. The ObjectMapper class is the main entry point for using jackson-databind for JSON parsing and generation. Below is an example code block that uses ObjectMapper to parse JSON data into a Java object:

 

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"John\", \"age\":30}";
Person person = mapper.readValue(json, Person.class);

 

  1. Serialization and Deserialization of Java objects: jackson-databind can be used to serialize Java objects into JSON and to deserialize JSON into Java objects. This is useful when working with APIs that require JSON data or when storing Java objects in JSON format. Below is an example code block that uses ObjectMapper to serialize a Java object into JSON:

 

ObjectMapper mapper = new ObjectMapper();
Person person = new Person("John", 30);
String json = mapper.writeValueAsString(person);


  1. Support for Polymorphic Type Handling: jackson-databind provides support for handling polymorphic types, which is useful when working with complex Java object hierarchies. This is done using the @JsonTypeInfo and @JsonSubTypes annotations. Below is an example code block that shows how to use these annotations:

 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Rectangle.class, name = "rectangle"),
    @JsonSubTypes.Type(value = Circle.class, name = "circle")
})
public abstract class Shape {
    public String name;
}

public class Rectangle extends Shape {
    public int width;
    public int height;
}

public class Circle extends Shape {
    public int radius;
}

These annotations can be used to specify the type information for a polymorphic type hierarchy. In this example, the @JsonTypeInfo annotation specifies that the type information is included as a property in the JSON data, and the @JsonSubTypes annotation specifies the concrete subtypes of the Shape class.

Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.