Eccezioni Jackson - Problemi e soluzioni

1. Panoramica

In questo tutorial, esamineremo le eccezioni Jackson più comuni : JsonMappingException e UnrecognizedPropertyException .

Infine, discuteremo brevemente di Jackson senza errori di questo tipo.

2. " JsonMappingException : Can Not Construct Instance Of"

2.1. Il problema

Innanzitutto, diamo un'occhiata a Jsonmappingexception: Can Not Construct Instance Of.

Questa eccezione viene generata se Jackson non è in grado di creare un'istanza della classe : ciò accade se la classe è astratta o è solo un'interfaccia .

Nell'esempio seguente, proviamo a deserializzare un'istanza dalla classe Zoo che ha una proprietà animal con tipo astratto 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() { } }

Quando proviamo a deserializzare una stringa JSON nell'istanza dello Zoo, viene generata "Jsonmappingexception: Can Not Construct Instance Of" come nell'esempio seguente:

@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); }

L' eccezione completa è:

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. Soluzioni

Possiamo risolvere il problema con una semplice annotazione - @JsonDeserialize sulla classe astratta:

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

Se abbiamo più di un sottotipo della classe astratta, dovremmo considerare di includere le informazioni sul sottotipo come mostrato in questo post: Ereditarietà con Jackson.

3. JsonMappingException : nessun costruttore adatto

3.1. Il problema

Ora, diamo un'occhiata alla comune Jsonmappingexception: Nessun costruttore adatto trovato per il tipo .

Questa eccezione viene generata se Jackson non può accedere al costruttore .

Nell'esempio seguente, la classe User non ha un costruttore predefinito:

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

Quando proviamo a deserializzare una stringa JSON per l'utente, viene generata un'eccezione "Jsonmappingexception: Nessun costruttore adatto trovato", come nell'esempio seguente:

@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); }

L' eccezione completa è:

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 soluzione

Per risolvere questo problema, aggiungi un costruttore predefinito come nell'esempio seguente:

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

Ora, quando deserializziamo, il processo funzionerà perfettamente:

@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 : il nome radice non corrisponde a quello previsto

4.1. Il problema

Successivamente, diamo un'occhiata a Jsonmappingexception: il nome della radice non corrisponde a quello previsto.

Questa eccezione viene generata se il JSON non corrisponde esattamente a ciò che Jackson sta cercando ; ad esempio, il JSON principale potrebbe essere avvolto come nell'esempio seguente:

@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); }

L' eccezione completa è:

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 soluzione

Possiamo risolvere questo problema utilizzando l'annotazione @JsonRootName , come nell'esempio seguente:

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

Quando proviamo a deserializzare il JSON avvolto, funziona correttamente:

@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 : nessun serializzatore trovato per la classe

5.1. Il problema

Ora, diamo un'occhiata a Jsonmappingexception: nessun serializzatore trovato per la classe.

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

In questo articolo, abbiamo approfondito i problemi di Jackson più comuni: eccezioni ed errori , esaminando le potenziali cause e le soluzioni per ciascuno.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata su Github : questo è un progetto basato su Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.