Exceptions de Jackson - Problèmes et solutions

Exceptions de Jackson - Problèmes et solutions

1. Vue d'ensemble

Dans ce didacticiel, nous allons passer en revuethe most common Jackson Exceptions - lesJsonMappingException etUnrecognizedPropertyException.

Enfin - nous allons brièvement discuter de Jackson aucune erreur de méthode de ce type.

Lectures complémentaires:

Jackson - Sérialiseur personnalisé

Contrôlez votre sortie JSON avec Jackson 2 à l'aide d'un sérialiseur personnalisé.

Read more

Exemples d'annotation de Jackson

Le noyau de Jackson est fondamentalement un ensemble d’annotations - assurez-vous de bien les comprendre.

Read more

Débuter avec la désérialisation personnalisée à Jackson

Utilisez Jackson pour mapper le JSON personnalisé à n’importe quel graphe d’entités java avec un contrôle total sur le processus de désérialisation.

Read more

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 siJackson can’t create an instance of the class - cela se produit si la classe estabstract ou c'est juste uninterface.

Dans l'exemple suivant - nous essayons de désérialiser une instance de la classeZoo qui a une propriétéanimal avecabstract typeAnimal:

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 JSONString en Zoo, cela lève l'exception «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);
}

Lefull exception est:

com.fasterxml.jackson.databind.JsonMappingException:
Can not construct instance of org.example.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.example.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 cet article:Inheritance with Jackson.

3. JsonMappingException: aucun constructeur approprié

3.1. Le problème

Voyons maintenant l’exception commune de Jsonmapping: aucun constructeur approprié found for type.

Cette exception est levée siJackson can’t access the constructor.

Dans l'exemple suivant, la classeUser 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 tant qu'exception, une exception «Jsonmappingexception: aucun constructeur approprié trouvé» est généré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);
}

Lefull exception est:

com.fasterxml.jackson.databind.JsonMappingException:
No suitable constructor found for type
[simple type, class org.example.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 parfaitement:

@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 de racine ne correspond pas aux attentes

4.1. Le problème

Ensuite, jetons un œil à Jsonmappingexception: le nom de la racine ne correspond pas aux attentes.

Cette exception est levée sithe JSON doesn’t match exactly what Jackson is looking for; 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);
}

Lefull exception est:

com.fasterxml.jackson.databind.JsonMappingException:
Root name 'user' does not match expected ('User') for type
 [simple type, class org.example.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 deserialize an instance while its properties and their getters are private.

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.example.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é deObjectMapper - 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 le source de la classe, nous pouvons également ajouter des getters à utiliser par Jackson.

6. JsonMappingException: impossible de désérialiser l'instance de

6.1. Le problème

Ensuite, jetons un coup d'œil à Jsonmappingexception: impossible de désérialiser l'instance de.

Cette exception est levée sithe wrong type is used lors de la désérialisation.

Dans l'exemple suivant - nous essayons de désérialiser unList deUser:

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

Lefull exception est:

com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of
  org.example.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. La solution

Nous pouvons résoudre ce problème en changeant le type deUser enList<User> - comme dans l'exemple suivant:

@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. Le problème

Maintenant, voyons lesUnrecognizedPropertyException.

Cette exception est levée s'il existe une chaîneunknown property in the JSON lors de la désérialisation.

Dans l'exemple suivant, nous essayons de désérialiser une chaîne JSON avec une propriété supplémentaire «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);
}

Lefull exception est:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "checked" (class org.example.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.example.jackson.dtos.User["checked"])
  at c.f.j.d.exc.UnrecognizedPropertyException.from(
    UnrecognizedPropertyException.java:51)

7.2. La solution

Nous pouvons résoudre ce problème en configurant lesObjectMapper - comme dans l'exemple suivant:

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

Ou nous pouvons utiliser l'annotation@JsonIgnoreProperties:

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

8. JsonParseException: caractère inattendu ("'(code 39))

8.1. Le problème

Ensuite - parlons deJsonParseException: Unexpected character (”' (code 39)).

Cette exception est levée sithe JSON String to be deserialized contains single quotes au lieu de guillemets doubles.

Dans l'exemple suivant, nous essayons de désérialiser une chaîne JSON contenant des guillemets simples:

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

Lefull exception est:

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

Lefull exception est:

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 this article, we did a deep dive in the most common Jackson problems – exceptions and errors, looking at the potential causes and at the solutions for each one.

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