Excepciones de Jackson: problemas y soluciones

1. Información general

En este tutorial, repasaremos las excepciones de Jackson más comunes: la excepción JsonMappingException y UnrecognizedPropertyException .

Finalmente, discutiremos brevemente Jackson sin tales errores de método.

2. " JsonMappingException : no se puede construir una instancia de"

2.1. El problema

Primero, echemos un vistazo a Jsonmappingexception: No se puede construir una instancia de.

Esta excepción se lanza si Jackson no puede crear una instancia de la clase ; esto sucede si la clase es abstracta o es solo una interfaz .

En el siguiente ejemplo, intentamos deserializar una instancia de la clase Zoo que tiene una propiedad animal con el tipo abstracto Animal :

public class Zoo { public Animal animal; public Zoo() { } } abstract class Animal { public String name; public Animal() { } } class Cat extends Animal { public int lives; public Cat() { } }

Cuando intentamos deserializar una instancia JSON String to Zoo, arroja la "Jsonmappingexception: Can Not Construct Instance Of" como en el siguiente ejemplo:

@Test(expected = JsonMappingException.class) public void givenAbstractClass_whenDeserializing_thenException() throws IOException { String json = "{"animal":{"name":"lacy"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(Zoo.class).readValue(json); }

La excepción completa es:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.baeldung.jackson.exception.Animal, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information at [Source: {"animal":{"name":"lacy"}}; line: 1, column: 2] (through reference chain: org.baeldung.jackson.exception.Zoo["animal"]) at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

2.2. Soluciones

Podemos resolver el problema con una anotación simple: @JsonDeserialize en la clase abstracta:

@JsonDeserialize(as = Cat.class) abstract class Animal {...}

Si tenemos más de un subtipo de la clase abstracta, entonces deberíamos considerar incluir información de subtipo como se muestra en esta publicación: Herencia con Jackson.

3. JsonMappingException : ningún constructor adecuado

3.1. El problema

Ahora, veamos la excepción común de Jsonmappingexception: No se encontró ningún constructor adecuado para el tipo .

Esta excepción se lanza si Jackson no puede acceder al constructor .

En el siguiente ejemplo, la clase User no tiene un constructor predeterminado:

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

Cuando intentamos deserializar una cadena JSON para el usuario, se lanza una excepción "Jsonmappingexception: No se encontró un constructor adecuado", como en el siguiente ejemplo:

@Test(expected = JsonMappingException.class) public void givenNoDefaultConstructor_whenDeserializing_thenException() throws IOException { String json = "{"id":1,"name":"John"}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

La excepción completa es:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class org.baeldung.jackson.exception.User]: can not instantiate from JSON object (need to add/enable type information?) at [Source: {"id":1,"name":"John"}; line: 1, column: 2] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

3.2. La solución

Para resolver este problema, simplemente agregue un constructor predeterminado como en el siguiente ejemplo:

public class User { public int id; public String name; public User() { super(); } public User(int id, String name) { this.id = id; this.name = name; } }

Ahora, cuando deserializamos, el proceso funcionará bien:

@Test public void givenDefaultConstructor_whenDeserializing_thenCorrect() throws IOException { String json = "{"id":1,"name":"John"}"; ObjectMapper mapper = new ObjectMapper(); User user = mapper.reader() .forType(User.class).readValue(json); assertEquals("John", user.name); }

4. JsonMappingException : el nombre de la raíz no coincide con el esperado

4.1. El problema

A continuación, echemos un vistazo a Jsonmappingexception: el nombre de la raíz no coincide con lo esperado.

Esta excepción se lanza si JSON no coincide exactamente con lo que busca Jackson ; por ejemplo, el JSON principal podría ajustarse como en el siguiente ejemplo:

@Test(expected = JsonMappingException.class) public void givenWrappedJsonString_whenDeserializing_thenException() throws IOException { String json = "{"user":{"id":1,"name":"John"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); mapper.reader().forType(User.class).readValue(json); }

La excepción completa es:

com.fasterxml.jackson.databind.JsonMappingException: Root name 'user' does not match expected ('User') for type [simple type, class org.baeldung.jackson.dtos.User] at [Source: {"user":{"id":1,"name":"John"}}; line: 1, column: 2] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148) 

4.2. La solución

Podemos resolver este problema usando la anotación @JsonRootName , como en el siguiente ejemplo:

@JsonRootName(value = "user") public class UserWithRoot { public int id; public String name; }

Cuando intentamos deserializar el JSON envuelto, funciona correctamente:

@Test public void givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect() throws IOException { String json = "{"user":{"id":1,"name":"John"}}"; ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); UserWithRoot user = mapper.reader() .forType(UserWithRoot.class) .readValue(json); assertEquals("John", user.name); }

5. JsonMappingException : No se encontró serializador para la clase

5.1. El problema

Ahora, echemos un vistazo a Jsonmappingexception: No se encontró serializador para la clase.

This exception is thrown if you try to serialize an instance while its properties and their getters are private.

In the following example – we try to serialize a “UserWithPrivateFields“:

public class UserWithPrivateFields { int id; String name; }

When we try to serialize an instance of “UserWithPrivateFields” – an Exception “Jsonmappingexception: No Serializer Found for Class” is thrown as in the following example:

@Test(expected = JsonMappingException.class) public void givenClassWithPrivateFields_whenSerializing_thenException() throws IOException { UserWithPrivateFields user = new UserWithPrivateFields(1, "John"); ObjectMapper mapper = new ObjectMapper(); mapper.writer().writeValueAsString(user); }

The full exception is:

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.baeldung.jackson.exception.UserWithPrivateFields and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) at c.f.j.d.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)

5.2. The Solution

We can solve this problem by configuring the ObjectMapper visibility – as in the following example:

@Test public void givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect() throws IOException { UserWithPrivateFields user = new UserWithPrivateFields(1, "John"); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); String result = mapper.writer().writeValueAsString(user); assertThat(result, containsString("John")); }

Or using the annotation @JsonAutoDetect – as in the following example:

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

Of course, if we do have the option to modify the source of the class, we can also add in getters for Jackson to use.

6. JsonMappingException: Can Not Deserialize Instance Of

6.1. The Problem

Next – let's take a look at Jsonmappingexception: Can Not Deserialize Instance Of.

This exception is thrown if the wrong type is used while deserializing.

In the following example – we are trying to deserialize a List of User:

@Test(expected = JsonMappingException.class) public void givenJsonOfArray_whenDeserializing_thenException() throws JsonProcessingException, IOException { String json = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.baeldung.jackson.dtos.User out of START_ARRAY token at [Source: [{"id":1,"name":"John"},{"id":2,"name":"Adam"}]; line: 1, column: 1] at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

6.2. The Solution

We can solve this problem by changing the type from User to List – as in the following example:

@Test public void givenJsonOfArray_whenDeserializing_thenCorrect() throws JsonProcessingException, IOException { String json = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]"; ObjectMapper mapper = new ObjectMapper(); List users = mapper.reader() .forType(new TypeReference
    
     () {}) .readValue(json); assertEquals(2, users.size()); }
    

7. UnrecognizedPropertyException

7.1. The Problem

Now – let's see the UnrecognizedPropertyException.

This exception is thrown if there is an unknown property in the JSON String while deserializing.

In the following example – we try to deserialize a JSON String with extra property “checked“:

@Test(expected = UnrecognizedPropertyException.class) public void givenJsonStringWithExtra_whenDeserializing_thenException() throws IOException { String json = "{"id":1,"name":"John", "checked":true}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader().forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "checked" (class org.baeldung.jackson.dtos.User), not marked as ignorable (2 known properties: "id", "name"]) at [Source: {"id":1,"name":"John", "checked":true}; line: 1, column: 38] (through reference chain: org.baeldung.jackson.dtos.User["checked"]) at c.f.j.d.exc.UnrecognizedPropertyException.from( UnrecognizedPropertyException.java:51)

7.2. The Solution

We can solve this problem by configuring the ObjectMapper – as in the following example:

@Test public void givenJsonStringWithExtra_whenConfigureDeserializing_thenCorrect() throws IOException { String json = "{"id":1,"name":"John", "checked":true}"; ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); User user = mapper.reader().forType(User.class).readValue(json); assertEquals("John", user.name); }

Or we can use the annotation @JsonIgnoreProperties:

@JsonIgnoreProperties(ignoreUnknown = true) public class User {...}

8. JsonParseException: Unexpected Character (”' (code 39))

8.1. The Problem

Next – let's discuss JsonParseException: Unexpected character (”' (code 39)).

This exception is thrown if the JSON String to be deserialized contains single quotes instead of double quotes.

In the following example – we try to deserialize a JSON String containing single quotes:

@Test(expected = JsonParseException.class) public void givenStringWithSingleQuotes_whenDeserializing_thenException() throws JsonProcessingException, IOException { String json = "{'id':1,'name':'John'}"; ObjectMapper mapper = new ObjectMapper(); mapper.reader() .forType(User.class).readValue(json); }

The full exception is:

com.fasterxml.jackson.core.JsonParseException: Unexpected character (''' (code 39)): was expecting double-quote to start field name at [Source: {'id':1,'name':'John'}; line: 1, column: 3] at c.f.j.core.JsonParser._constructError(JsonParser.java:1419)

8.2. The Solution

We can solve this by configuring the ObjectMapper to allow single quotes:

@Test public void givenStringWithSingleQuotes_whenConfigureDeserializing_thenCorrect() throws JsonProcessingException, IOException { String json = "{'id':1,'name':'John'}"; JsonFactory factory = new JsonFactory(); factory.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); ObjectMapper mapper = new ObjectMapper(factory); User user = mapper.reader().forType(User.class) .readValue(json); assertEquals("John", user.name); }

9. Jackson NoSuchMethodError

Finally – let's quickly discuss the Jackson “No such method” errors.

When java.lang.NoSuchMethodError Exception is thrown, it is usually because you have multiple (and incompatible) versions of Jackson jars on your classpath.

The full exception is:

java.lang.NoSuchMethodError: com.fasterxml.jackson.core.JsonParser.getValueAsString()Ljava/lang/String; at c.f.j.d.deser.std.StringDeserializer.deserialize(StringDeserializer.java:24)

10. Conclusion

En este artículo, hicimos un análisis profundo de los problemas más comunes de Jackson: excepciones y errores , analizando las causas potenciales y las soluciones para cada uno.

La implementación de todos estos ejemplos y fragmentos de código se puede encontrar en Github ; este es un proyecto basado en Maven, por lo que debería ser fácil de importar y ejecutar como está.