Exceptions Jackson - Problèmes et solutions

1. Vue d'ensemble

Dans ce didacticiel, nous allons passer en revue les exceptions Jackson les plus courantes - les exceptions JsonMappingException et UnrecognizedPropertyException .

Enfin - nous discuterons brièvement de Jackson pas de telles erreurs de méthode.

2. " JsonMappingException : impossible de construire l'instance de"

2.1. Le problème

Tout d'abord, jetons un coup d'œil à Jsonmappingexception: Impossible de construire l'instance de.

Cette exception est levée si Jackson ne peut pas créer une instance de la classe - cela se produit si la classe est abstraite ou simplement une interface .

Dans l'exemple suivant, nous essayons de désérialiser une instance de la classe Zoo qui a une propriété animal avec le type abstrait 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() { } }

Lorsque nous essayons de désérialiser une instance JSON String to Zoo, cela lève le «Jsonmappingexception: Can Not Construct Instance Of» comme dans l'exemple suivant:

@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' exception complète est:

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

Nous pouvons résoudre le problème avec une simple annotation - @JsonDeserialize sur la classe abstraite:

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

Si nous avons plus d'un sous-type de la classe abstraite, nous devrions envisager d'inclure les informations de sous-type comme indiqué dans ce post: Héritage avec Jackson.

3. JsonMappingException : aucun constructeur approprié

3.1. Le problème

Maintenant, regardons l'exception commune Jsonmappingexception: aucun constructeur approprié trouvé pour le type .

Cette exception est levée si Jackson ne peut pas accéder au constructeur .

Dans l'exemple suivant, la classe User n'a pas de constructeur par défaut:

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

Lorsque nous essayons de désérialiser une chaîne JSON en utilisateur, une exception «Jsonmappingexception: No Convient Constructor found» est lancée - comme dans l'exemple suivant:

@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' exception complète est:

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 solution

Pour résoudre ce problème, ajoutez simplement un constructeur par défaut comme dans l'exemple suivant:

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

Maintenant, lorsque nous désérialisons - le processus fonctionnera très 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 : le nom racine ne correspond pas aux attentes

4.1. Le problème

Ensuite, jetons un coup d'œil à Jsonmappingexception: le nom de racine ne correspond pas à l'attente.

Cette exception est levée si le JSON ne correspond pas exactement à ce que Jackson recherche ; par exemple, le JSON principal pourrait être encapsulé comme dans l'exemple suivant:

@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' exception complète est:

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 solution

Nous pouvons résoudre ce problème en utilisant l'annotation @JsonRootName - comme dans l'exemple suivant:

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

Lorsque nous essayons de désérialiser le JSON encapsulé, cela fonctionne correctement:

@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 : aucun sérialiseur trouvé pour la classe

5.1. Le problème

Maintenant, jetons un coup d'œil à Jsonmappingexception: aucun sérialiseur trouvé pour la classe.

Cette exception est levée si vous essayez de sérialiser une instance alors que ses propriétés et leurs getters sont privés .

Dans l'exemple suivant - nous essayons de sérialiser un « UserWithPrivateFields »:

public class UserWithPrivateFields { int id; String name; }

Lorsque nous essayons de sérialiser une instance de « UserWithPrivateFields », une exception «Jsonmappingexception: No Serializer Found for Class» est lancée comme dans l'exemple suivant:

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

L'exception complète est:

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. La solution

Nous pouvons résoudre ce problème en configurant la visibilité d' ObjectMapper - comme dans l'exemple suivant:

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

Ou en utilisant l'annotation @JsonAutoDetect - comme dans l'exemple suivant:

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

Bien sûr, si nous avons la possibilité de modifier la source de la classe, nous pouvons également ajouter des getters à utiliser par Jackson.

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

Dans cet article, nous avons approfondi les problèmes les plus courants de Jackson - exceptions et erreurs , en examinant les causes potentielles et les solutions pour chacun.

L'implémentation de tous ces exemples et extraits de code peut être trouvée sur Github - il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.