1
votes

Jackson InvalidDefinitionException: Impossible de construire l'instance car aucun constructeur par défaut sans arguments n'a été trouvé

J'ai une application qui utilise Spring Boot pour fournir la capacité REST. Je rencontre un problème lors de la désérialisation d'une réponse POST en POJO. L'exception est la suivante:

RequestStatus statusMsg = template.getForObject("http://localhost:8080/rst/missionPlanning/data", RequestStatus.class);

Le type BoundedList fait partie d'une API qui est générée à partir d'un schéma XML utilisant XJC. Je n'ai aucun contrôle sur la façon dont cette classe est générée. Il s'avère que c'est une sous-classe de java.util.ArrayList et n'a qu'un seul constructeur défini:

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer objectMapperCustomizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
      @Override
      public void customize(Jackson2ObjectMapperBuilder builder) {
        builder.mixIn(BoundedList.class, BoundedListMixin.class);
      }
    };
  }

Il ne définit pas de constructeur sans argument , ce dont l'exception semble se plaindre.

Puisque je ne peux pas modifier cette classe et qu'elle fait partie intégrante de l'API que j'utilise, que puis-je faire pour contourner ce problème? Puis-je fournir une sorte de classe / interface personnalisée qui satisfera la liaison de données Jackson? Une autre solution possible?

MISE À JOUR :

J'ai essayé un certain nombre de suggestions basées sur les réponses fournies ci-dessous. Aucun d'eux ne fonctionne. J'étudie l'approche "mix-in" et j'avoue que je ne comprends pas très bien comment elle est censée fonctionner. Les nombreux articles que j'ai lus sont écrits simplement, mais cela semble toujours être un peu de "magie noire".

Dans tous les cas, voici des extraits de ce que j'ai essayé de faire en fonction de ce que j'ai 'ai lu:

La classe "mix-in":

@Configuration
@EnableAsync
@EnableScheduling
@ComponentScan("<package>")
public class ServiceConfigurer {
  @Bean
  @Primary
  public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper mapper = builder.createXmlMapper(false).build();
    mapper.addMixIn(BoundedList.class, BoundedListMixin.class);
    return mapper;
  }
}

Ce qui a été ajouté à une classe de configuration existante:

public abstract class BoundedListMixin {
  @JsonCreator
  public BoundedListMixin(@JsonProperty("minOccurs") int minOccurs,
      @JsonProperty("maxOccurs") int maxOccurs) {}
}

Je peux confirmer que la méthode objectMapper () dans la classe de configuration est appelée via le débogueur.

Ce lien ( https://dzone.com/articles/jackson-mixin-to-the-rescue ) introduit une autre facette, mais hélas, cela ne fonctionne pas non plus.

MISE À JOUR :

J'ai remplacé le objectMapper () code > méthode dans ServiceConfigurer (ci-dessus) avec ce qui suit:

public BoundedList(int minOccurs, int maxOccurs) {
    super();
    this.minOccurs = minOccurs;
    this.maxOccurs = maxOccurs;
}

J'obtiens TOUJOURS le même problème. Le problème doit être lié à un autre problème.

REMARQUE :

Je dois également préciser que ce problème se manifeste lorsque j'ai créé un GET appel. J'ai un point de terminaison REST simple qui demande simplement à un service de renvoyer un POJO. Curieusement, si j'envoie la demande depuis un navigateur, l'objet est renvoyé au format JSON et rendu. Mais, quand il est appelé à partir du code, j'obtiens l'exception ci-dessus.

L'appel GET:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [collection type; class uci.BoundedList, contains [simple type, class java.lang.Object]]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `uci.BoundedList` (no Creators, like default construct, exist): no default no-arguments constructor found


0 commentaires

3 Réponses :


1
votes

Le moyen le plus simple est d'ajouter des annotations Jackson au POJO pour lui indiquer comment désérialiser. Cette question le décrit assez bien.

Cependant, comme votre POJO est généré automatiquement et que vous ne pouvez pas le modifier, il existe 2 autres options:

  1. Utilisez un désérialiseur personnalisé pour analyser manuellement la charge utile JSON dans une instance de votre POJO < / li>
  2. Utilisez un mix-in pour décorer le POJO avec les annotations souhaitées

0 commentaires

0
votes

Vous pouvez le mapper à un type différent, puis le convertir en une liste délimitée.


0 commentaires

2
votes

Jackson a besoin d'un constructeur par défaut sans paramètres ni annotations sur les paramètres du constructeur pour savoir quel champ de l'objet JSON doit être mappé au paramètre. Quelque chose comme ceci:

objectMapper.addMixInAnnotations(BoundedList.class, MixIn.class);

Lorsque vous ne pouvez pas changer de classe, vous pouvez utiliser Jackson MixIns

abstract class MixIn {
   MixIn(@JsonProperty("min") int minOccurs, @JsonProperty("max") int maxOccurs) { }
}

et le configurer sur le mappeur d'objets comme ceci:

@JsonCreator
public BoundedList(@JsonProperty("min") int minOccurs, @JsonProperty("max") int maxOccurs) {
   // your code
}

Voir https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations pour plus de détails.


7 commentaires

L'approche "MixIn" que vous avez suggérée (également suggérée par @Mike ci-dessous) semble très intéressante et semble pouvoir résoudre mon problème. Ce dont je ne suis pas sûr, c'est que cela semble être quelque chose qui est enregistré avec le Jackson ObjectMapper . Je n'utilise pas explicitement ObjectMapper dans ma situation. J'utilise un RestTemplate pour faire une requête POST et l'objet renvoyé est l'endroit où le problème se produit (du moins c'est ce que je pense). Comment pourrais-je (puis-je?) Impliquer ObjectMapper (et le mix-in) dans tout cela?


Voici l'appel: ResponseEntity response = template.postForEntity ("http: // localhost: 8080 / rst / missionPla‌ nning / generateRoute" ‌, entity, CommandStatus.class);


Vous pouvez trouver un exemple ici: gdpotter.com/2017/05/ 24 / custom-spring-mvc-jackson (vous en trouverez probablement beaucoup plus sur Google). L'idée est que vous enregistrez votre propre bean ObjectMapper. Spring Boot vous aide avec cela.


J'ai essayé de faire comme suggéré (en fait deux façons différentes en fonction de ce que j'ai trouvé en recherchant sur Google) et aucune d'entre elles ne semble fonctionner. Je reçois toujours la même exception. :(


Voici un lien qui semble tout à fait à propos de ma situation. Cependant, cela ne fonctionne toujours pas. blogs.jbisht.com/blogs/ 2016/09/12 /…


@JosephGagnon Spring boot fournit un mappeur d'objets par défaut. Il peut être personnalisé à l'aide de diverses configurations d'application, et vous pouvez également créer un bean de type Jackson2ObjectMapperBuilderCustomizer dans votre application pour appliquer les mix-ins et autres personnalisations.


@JosephGagnon Cette réponse a un bon exemple: stackoverflow.com/a/48519868/657224 ... vous devrez appeler jacksonObjectMapperBuilder.mixIn (...) pour définir votre mix personnalisé dans la classe pour BoundedList.