JsonMappingException: aucun constructeur approprié trouvé pour le type [type simple, classe]: ne peut pas instancier à partir d'un objet JSON

438

J'obtiens l'erreur suivante lorsque j'essaie d'obtenir une demande JSON et de la traiter:

org.codehaus.jackson.map.JsonMappingException: aucun constructeur approprié trouvé pour le type [type simple, classe com.myweb.ApplesDO]: ne peut pas instancier à partir de l'objet JSON (besoin d'ajouter / activer les informations de type?)

Voici le JSON que j'essaie d'envoyer:

{
  "applesDO" : [
    {
      "apple" : "Green Apple"
    },
    {
      "apple" : "Red Apple"
    }
  ]
}

Dans Controller, j'ai la signature de méthode suivante:

@RequestMapping("showApples.do")
public String getApples(@RequestBody final AllApplesDO applesRequest){
    // Method Code
}

AllApplesDO est un wrapper d'ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

    public List<ApplesDO> getApplesDO() {
        return applesDO;
    }

    public void setApplesDO(List<ApplesDO> applesDO) {
        this.applesDO = applesDO;
    }
}

ApplesDO:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String appl) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom){
        //constructor Code
    }
}

Je pense que Jackson est incapable de convertir JSON en objets Java pour les sous-classes. Veuillez aider avec les paramètres de configuration pour Jackson pour convertir JSON en objets Java. J'utilise Spring Framework.

EDIT: inclus le bogue majeur à l'origine de ce problème dans l'exemple de classe ci-dessus.

Lucky Murari
la source
2
Je ne vois aucune sous-classe dans le code ci-dessus, est-ce ce code que vous essayez ou que vous inventez un exemple plus simple?
gkamal
J'ai ajouté une réponse avec quelques explications supplémentaires sur la façon dont cela fonctionne. Fondamentalement, vous devez vous rendre compte que Java ne conserve pas les noms des arguments de méthode lors de l'exécution.
Vlasec

Réponses:

565

Alors, j'ai finalement réalisé quel était le problème. Ce n'est pas un problème de configuration Jackson comme je le doutais.

En fait, le problème était dans la classe ApplesDO :

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }
}

Un constructeur personnalisé a été défini pour la classe, ce qui en fait le constructeur par défaut. L'introduction d'un constructeur factice a fait disparaître l'erreur:

public class ApplesDO {

    private String apple;

    public String getApple() {
        return apple;
    }

    public void setApple(String apple) {
        this.apple = apple;
    }

    public ApplesDO(CustomType custom) {
        //constructor Code
    }

    //Introducing the dummy constructor
    public ApplesDO() {
    }

}
Lucky Murari
la source
Puis-je demander d'où vient CustomType. J'essaie une structure comme celle-ci, mais je suis absolument novice en Java.
andho
181
Vous pouvez utiliser jackson avec des classes internes (imbriquées), la sérialisation fonctionne très bien dans ce cas. La seule pierre d'achoppement est que la classe interne doit être marquée comme "statique" pour que la désérialisation fonctionne correctement. Voir l'exlanation ici: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell
3
Quelqu'un pourrait-il expliquer pourquoi cela se produit? J'ai eu une erreur très similaire. Je pensais que tous les bons constructeurs étaient en place, mais ne pouvaient pas désérialiser. Cela n'a fonctionné qu'après avoir ajouté un constructeur factice, après avoir lu ce post.
user8658912
6
@Suman Je n'appellerais pas cela un constructeur factice - c'est juste le constructeur par défaut. Non seulement il est parfaitement valide, mais il est nécessaire pour de nombreux types de traitement de type Java Bean. (Et, bien sûr, cela m'a fait trébucher. :-))
fool4jesus
2
Si vous ne voulez pas ajouter de constructeur par défaut (par exemple, lorsque vous traitez avec des objets immuables). Vous devrez indiquer le constructeur ou la méthode d'usine à utiliser pour instancier l'objet à l'aide de l'annotation JsonCreator.
Rahul
376

Cela se produit pour ces raisons:

  1. votre classe intérieure doit être définie comme statique

    private static class Condition {  //jackson specific    
    }
    
  2. Il se peut que vous n'ayez pas de constructeur par défaut dans votre classe ( MISE À JOUR: Cela ne semble pas être le cas)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
    
  3. Il se peut que vos Setters ne soient pas définis correctement ou ne soient pas visibles (par exemple, setter privé)

azerafati
la source
95
la classe statique a fait la différence dans mon cas. Merci!
jalogar
3
Et bien sûr, vous n'avez pas besoin de déclarer un constructeur par défaut vide, sans argument, Java le fait pour vous ! (Tant que vous ne définissez aucun autre constructeur.)
Jonik
1
@ Jonik, c'est vrai! ma réponse est ancienne, si je me souviens bien, puisque Jackson utilise la réflexion pour accéder à la classe interne, je suppose qu'il était nécessaire de définir le constructeur par défaut (cela pourrait également ne pas être le cas dans les versions plus récentes), mais comme je ne le suis pas vous pourriez être correct.
azerafati
6
Oui, mon expérience est avec Jackson 2.4.4: en effet, le constructeur implicite par défaut de Java est suffisant. Vous devez écrire explicitement le constructeur sans argument uniquement si vous avez défini d' autres constructeurs qui acceptent des arguments (c'est-à-dire lorsque Java ne génère pas celui sans argument pour vous).
Jonik
2
Toujours avec moi, la classe statique a sauvé la mise.
Simon
58

Je voudrais ajouter une autre solution à cela qui ne nécessite pas de constructeur factice. Étant donné que les constructeurs factices sont un peu désordonnés et confus par la suite. Nous pouvons fournir un constructeur sûr et en annotant les arguments du constructeur, nous permettons à jackson de déterminer la correspondance entre le paramètre du constructeur et le champ.

donc ce qui suit fonctionnera également. Notez que la chaîne à l'intérieur de l'annotation doit correspondre au nom du champ.

import com.fasterxml.jackson.annotation.JsonProperty;
public class ApplesDO {

        private String apple;

        public String getApple() {
            return apple;
        }

        public void setApple(String apple) {
            this.apple = apple;
        }

        public ApplesDO(CustomType custom){
            //constructor Code
        }

        public ApplesDO(@JsonProperty("apple")String apple) {
        }

}
PiersyP
la source
Cette solution a fait l'affaire. Je veux juste mentionner ce qui suit, j'avais un constructeur, mais le nom des paramètres était différent de celui des paramètres d'instance, donc il ne pouvait pas être mappé. L'ajout de l'annotation l'a résolu, mais renommer les paramètres aurait probablement fonctionné également.
Fico
Que faire si le paramètre constructeur n'est pas dans la réponse? Peut-il être injecté d'une autre manière?
Eddie Jaoude
Les seules données importantes envoyées ici sont la pomme String qui est la réponse.
PiersyP
1
Cela m'a été utile parce que je voulais que mon objet soit immuable, donc un constructeur factice n'était pas une option.
Jonathan Pullano
Dans mon cas, il faut également ajouter @JsonCreatorle constructeur avec @JsonProperty.
m1ld
31

Quand j'ai rencontré ce problème, c'était le résultat de l'utilisation d'une classe interne pour servir de DO. La construction de la classe interne (en silence) nécessitait une instance de la classe englobante - qui n'était pas disponible pour Jackson.

Dans ce cas, le déplacement de la classe interne vers son propre fichier .java a résolu le problème.

jmarks
la source
6
Alors que le déplacement de la classe interne vers son propre fichier .java fonctionne, l'ajout du modificateur statique résout également le problème mentionné dans la réponse de @ bludream.
jmarks
20

Généralement, cette erreur vient du fait que nous ne faisons pas de constructeur par défaut, mais dans mon cas, le problème ne venait que parce que j'ai créé la classe d'objets utilisée dans la classe parente. Cela a gâché toute ma journée.

alok
la source
Il suffit de créer la classe imbriquée static.
paresseux
13

Règle générale : ajoutez un constructeur par défaut pour chaque classe que vous avez utilisée comme classe de mappage. Vous avez raté cela et le problème se pose!
Ajoutez simplement le constructeur par défaut et cela devrait fonctionner.

Badal
la source
belle réponse. Merci. J'ai sauvé ma journée.
Steve
9

Pouvez-vous s'il vous plaît tester cette structure. Si je me souviens bien, vous pouvez l'utiliser de cette façon:

{
    "applesRequest": {
        "applesDO": [
            {
                "apple": "Green Apple"
            },
            {
                "apple": "Red Apple"
            }
        ]
    }
}

Deuxièmement, veuillez ajouter un constructeur par défaut à chaque classe, cela pourrait également aider.

danny.lesnik
la source
Ne fonctionne pas: erreur suivante: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: champ non reconnu & quot; applesRequest & quot; (Class com.smartshop.dao.AllApplesDO), non marqué comme ignorable"
Lucky Murari
Auparavant, il ne permettait pas au moins une erreur pour AllApplesDO et ne lance que pour la classe fermée .. maintenant, il lance pour la première classe elle
Lucky Murari
Besoin d'un constructeur par défaut. Merci!
Planky
cela ne devrait-il pas être choisi comme réponse CORRECTE?
dent rapide
7

Vous devez créer un constructeur vide factice dans notre classe de modèle. Ainsi, lors du mappage de json, il est défini par la méthode setter.

Suresh Patil
la source
Ceci est la solution.
David Kobia
C'est aussi le cas pour moi. J'avais de nombreux constructeurs différents pour mon objet, et j'ai donc créé un autre vide qui semble être utilisé par jackson.
Alessandro Roaro
5

Si vous commencez à annoter le constructeur, vous devez annoter tous les champs.

Notez que mon champ Staff.name est mappé sur "ANOTHER_NAME" dans la chaîne JSON.

     String jsonInString="{\"ANOTHER_NAME\":\"John\",\"age\":\"17\"}";
     ObjectMapper mapper = new ObjectMapper();
     Staff obj = mapper.readValue(jsonInString, Staff.class);
     // print to screen

     public static class Staff {
       public String name;
       public Integer age;
       public Staff() {         
       }        

       //@JsonCreator - don't need this
       public Staff(@JsonProperty("ANOTHER_NAME") String   n,@JsonProperty("age") Integer a) {
        name=n;age=a;
       }        
    }
Vortex
la source
4

Vous devez comprendre les options dont Jackson dispose pour la désérialisation. En Java, les noms des arguments de méthode ne sont pas présents dans le code compilé. C'est pourquoi Jackson ne peut généralement pas utiliser de constructeurs pour créer un objet bien défini avec tout ce qui est déjà défini.

Donc, s'il y a un constructeur vide et qu'il y a aussi des setters, il utilise le constructeur et les setters vides. S'il n'y a pas de setters, de la magie noire (réflexions) est utilisée pour le faire.

Si vous souhaitez utiliser un constructeur avec Jackson, vous devez utiliser les annotations mentionnées par @PiersyP dans sa réponse. Vous pouvez également utiliser un modèle de générateur. Si vous rencontrez des exceptions, bonne chance. La gestion des erreurs dans Jackson est un gros problème, il est difficile de comprendre ce charabia dans les messages d'erreur.

Vlasec
la source
La raison pour laquelle vous avez besoin d'un "constructeur par défaut sans argument" FooClass () est probablement parce que Spring suit la spécification JavaBean, qui requiert que cela fonctionne pour le marshaling et le démarshalling automatiquement lors de la sérialisation et de la désérialisation des objets.
atom88
Eh bien, la sérialisation et la désérialisation Java en flux binaire n'est pas de toute façon la question. Il est donc bon que Jackson propose plusieurs modèles à utiliser avec la désérialisation. J'aime particulièrement le modèle de générateur car il permet à l'objet résultant d'être immuable.
Vlasec
3

En ce qui concerne la dernière publication, j'ai eu le même problème lorsque l'utilisation de Lombok 1.18. * A généré le problème.

Ma solution a été d'ajouter @NoArgsConstructor (constructeur sans paramètres), car @Data inclut par défaut @RequiredArgsConstructor (constructeur avec paramètres).

Documentation lombok https://projectlombok.org/features/all

Cela résoudrait le problème:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
@NoArgsConstructor
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}
Felipe Ceballos
la source
0

Le défaut de sérialiseurs / désérialiseurs jackson personnalisés pourrait également être le problème. Bien que ce ne soit pas votre cas, cela vaut la peine d'être mentionné.

J'ai fait face à la même exception et c'était le cas.

Artem Novikov
la source
0

Pour moi, cela fonctionnait auparavant, mais la mise à niveau des bibliothèques a provoqué l'apparition de ce problème. Le problème était d'avoir une classe comme celle-ci:

package example.counter;

import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
public class CounterRequest {
    @NotNull
    private final Integer int1;

    @NotNull
    private final Integer int2;
}

Utilisation de lombok:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version>
</dependency>

Se replier sur

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>

Correction du problème. Je ne sais pas pourquoi, mais je voulais le documenter pour l'avenir.

eis
la source