JsonMappingException: Kein geeigneter Konstruktor für Typ [einfacher Typ, Klasse] gefunden: Kann nicht aus JSON-Objekt instanziieren

437

Beim Versuch, eine JSON-Anforderung abzurufen und zu verarbeiten, wird folgende Fehlermeldung angezeigt:

org.codehaus.jackson.map.JsonMappingException: Kein geeigneter Konstruktor für Typ [einfacher Typ, Klasse com.myweb.ApplesDO] gefunden: Kann nicht vom JSON-Objekt instanziieren (müssen Typinformationen hinzugefügt / aktiviert werden?)

Hier ist der JSON, den ich senden möchte:

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

In Controller habe ich die folgende Methodensignatur:

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

AllApplesDO ist ein Wrapper von 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
    }
}

Ich denke, dass Jackson JSON nicht in Java-Objekte für Unterklassen konvertieren kann. Bitte helfen Sie mit den Konfigurationsparametern für Jackson, um JSON in Java-Objekte zu konvertieren. Ich benutze Spring Framework.

BEARBEITEN: Der Hauptfehler, der dieses Problem verursacht, wurde in die obige Beispielklasse aufgenommen. Bitte suchen Sie nach einer akzeptierten Antwort, um eine Lösung zu finden.

Glücklicher Murari
quelle
2
Ich sehe im obigen Code keine Unterklassen. Versuchen Sie diesen Code oder erfinden Sie ein einfacheres Beispiel?
Gkamal
Ich habe eine Antwort mit einer weiteren Erklärung hinzugefügt, wie es funktioniert. Grundsätzlich müssen Sie erkennen, dass Java zur Laufzeit keine Methodenargumentnamen behält.
Vlasec

Antworten:

565

Endlich wurde mir klar, wo das Problem liegt. Es ist kein Jackson-Konfigurationsproblem, wie ich bezweifelte.

Eigentlich war das Problem in der ApplesDO- Klasse:

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
    }
}

Für die Klasse wurde ein benutzerdefinierter Konstruktor definiert, der sie zum Standardkonstruktor macht. Die Einführung eines Dummy-Konstruktors hat den Fehler behoben:

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() {
    }

}
Glücklicher Murari
quelle
Darf ich fragen, woher CustomType kommt? Ich versuche eine Struktur wie diese, aber ich bin absolut neu in Java.
Andho
181
Sie können Jackson mit inneren (verschachtelten) Klassen verwenden. In diesem Fall funktioniert die Serialisierung einwandfrei. Der einzige Stolperstein ist, dass die innere Klasse als "statisch" markiert werden muss, damit die Deserialisierung ordnungsgemäß funktioniert. Siehe Erklärung hier: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell
3
Könnte jemand bitte erklären, warum dies passiert? Ich hatte einen sehr ähnlichen Fehler. Ich dachte, alle richtigen Konstrukteure wären vorhanden, könnten aber nicht deserialisieren. Es hat erst funktioniert, nachdem ich nach dem Lesen dieses Beitrags einen Dummy-Konstruktor hinzugefügt habe.
user8658912
6
@ Human Ich würde dies nicht als Dummy-Konstruktor bezeichnen - es ist nur der Standardkonstruktor. Es ist nicht nur vollkommen gültig, sondern wird auch für viele Arten der Verarbeitung vom Typ Java Bean benötigt. (Und natürlich hat es mich gestolpert. :-))
dumm4jesus
2
Wenn Sie keinen Standardkonstruktor hinzufügen möchten (z. B. wenn Sie mit unveränderlichen Objekten arbeiten). Sie müssen angeben, welcher Konstruktor oder welche Factory-Methode zum Instanziieren des Objekts mithilfe der JsonCreator-Annotation verwendet werden soll.
Rahul
375

Dies geschieht aus folgenden Gründen:

  1. Ihre innere Klasse sollte als statisch definiert sein

    private static class Condition {  //jackson specific    
    }
  2. Möglicherweise haben Sie keinen Standardkonstruktor in Ihrer Klasse ( UPDATE: Dies scheint nicht der Fall zu sein).

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Es kann sein, dass Ihre Setter nicht richtig definiert oder nicht sichtbar sind (z. B. privater Setter).

Azerafati
quelle
95
Die statische Klasse machte in meinem Fall den Unterschied. Vielen Dank!
Jalogar
3
Und natürlich müssen Sie keinen leeren Standardkonstruktor ohne Argumente deklarieren, Java erledigt das für Sie ! (Solange Sie keine anderen Konstruktoren definieren.)
Jonik
1
@ Jonik, richtig! Meine Antwort ist alt, wenn ich mich richtig erinnere, da Jackson Reflexion verwendet, um zur inneren Klasse zu gelangen, war es wohl notwendig, den Standardkonstruktor zu definieren (könnte auch in neueren Versionen kein Fall sein), aber da ich es nicht bin sicher, dass Sie richtig sein könnten.
Azerafati
6
Ja, meine Erfahrung ist mit Jackson 2.4.4: In der Tat reicht Javas impliziter Standardkonstruktor aus. Sie müssen den Konstruktor no-args nur dann explizit schreiben, wenn Sie andere Konstruktoren definiert haben, die Argumente annehmen (dh wenn Java den Konstruktor no-args nicht für Sie generiert).
Jonik
2
Auch bei mir hat die statische Klasse den Tag gerettet.
Simon
58

Ich möchte eine weitere Lösung hinzufügen, für die kein Dummy-Konstruktor erforderlich ist. Da sind Dummy-Konstruktoren etwas chaotisch und anschließend verwirrend. Wir können einen sicheren Konstruktor bereitstellen und durch Annotieren der Konstruktorargumente erlauben wir Jackson, die Zuordnung zwischen Konstruktorparameter und Feld zu bestimmen.

Das Folgende wird also auch funktionieren. Beachten Sie, dass die Zeichenfolge in der Anmerkung mit dem Feldnamen übereinstimmen muss.

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
quelle
Diese Lösung hat es geschafft. Ich möchte nur Folgendes erwähnen: Ich hatte einen Konstruktor, aber der Name der Parameter war anders als der der Instanzparameter, sodass er nicht zugeordnet werden konnte. Das Hinzufügen der Anmerkung löste das Problem, aber das Umbenennen der Parameter hätte wahrscheinlich auch funktioniert.
Fico
Was ist, wenn der Konstruktorparameter nicht in der Antwort enthalten ist? Kann es auf andere Weise injiziert werden?
Eddie Jaoude
Die einzige wesentliche Daten , die hier gesendet wird, ist der String Apfel, ist die Antwort.
PiersyP
1
Dies war für mich nützlich, da ich wollte, dass mein Objekt unveränderlich ist, sodass ein Dummy-Konstruktor keine Option war.
Jonathan Pullano
In meinem Fall muss @JsonCreatorder Konstruktor auch mit hinzugefügt werden @JsonProperty.
m1ld
31

Als ich auf dieses Problem stieß, war es das Ergebnis des Versuchs, eine innere Klasse als DO zu verwenden. Der Aufbau der inneren Klasse (stillschweigend) erforderte eine Instanz der einschließenden Klasse - die Jackson nicht zur Verfügung stand.

In diesem Fall wurde das Problem behoben, indem die innere Klasse in eine eigene Java-Datei verschoben wurde.

jmarks
quelle
6
Während das Verschieben der inneren Klasse in eine eigene Java-Datei funktioniert, wird durch Hinzufügen des statischen Modifikators auch das in der Antwort von @ bludream erwähnte Problem behoben.
Jmarks
20

Im Allgemeinen tritt dieser Fehler auf, weil wir keinen Standardkonstruktor erstellen, aber in meinem Fall trat das Problem nur auf, weil ich eine verwendete Objektklasse innerhalb der übergeordneten Klasse erstellt habe. Das hat meinen ganzen Tag verschwendet.

alok
quelle
Es ist genug, um die verschachtelte Klasse zu machen static.
Faultier
13

Daumenregel : Fügen Sie für jede Klasse, die Sie als Zuordnungsklasse verwendet haben, einen Standardkonstruktor hinzu. Sie haben dies verpasst und es tritt ein Problem auf!
Fügen Sie einfach den Standardkonstruktor hinzu und es sollte funktionieren.

Badal
quelle
nette Antwort. Danke. hat meinen Tag gerettet.
Steve
9

Können Sie bitte diese Struktur testen? Wenn ich mich richtig erinnere, können Sie es folgendermaßen verwenden:

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

Zweitens fügen Sie jeder Klasse einen Standardkonstruktor hinzu, der ebenfalls hilfreich sein könnte.

danny.lesnik
quelle
Funktioniert nicht: Der folgende Fehler wird angezeigt: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Nicht erkanntes Feld" applesRequest "(Klasse com.smartshop.dao.AllApplesDO), nicht als ignorierbar markiert"
Lucky Murari
Früher war es zumindest nicht durch Fehler für AllApplesDO und wirft nur für die eingeschlossene Klasse. Jetzt wirft es für die erste Klasse selbst
Lucky Murari
Benötigte einen Standardkonstruktor. Vielen Dank!
Planky
Sollte dies nicht als RICHTIGE Antwort gewählt werden?
schneller Zahn
7

Sie müssen einen leeren Dummy-Konstruktor in unserer Modellklasse erstellen. Während Sie json zuordnen, wird er durch die Setter-Methode festgelegt.

Suresh Patil
quelle
Dies ist die Lösung.
David Kobia
Das ist auch bei mir der Fall. Ich hatte viele verschiedene Konstruktoren für mein Objekt und habe gerade einen weiteren leeren erstellt, der anscheinend von Jackson verwendet wird.
Alessandro Roaro
5

Wenn Sie mit dem Annotieren des Konstruktors beginnen, müssen Sie alle Felder mit Annotationen versehen.

Beachten Sie, dass mein Feld Staff.name in der JSON-Zeichenfolge "ANOTHER_NAME" zugeordnet ist.

     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;
       }        
    }
Wirbel
quelle
4

Sie müssen erkennen, welche Optionen Jackson für die Deserialisierung zur Verfügung hat. In Java sind Methodenargumentnamen im kompilierten Code nicht vorhanden. Aus diesem Grund kann Jackson im Allgemeinen keine Konstruktoren verwenden, um ein genau definiertes Objekt zu erstellen, bei dem bereits alles festgelegt ist.

Wenn es also einen leeren Konstruktor gibt und es auch Setter gibt, werden der leere Konstruktor und die Setter verwendet. Wenn es keine Setter gibt, wird etwas dunkle Magie (Reflexionen) verwendet, um dies zu tun.

Wenn Sie einen Konstruktor mit Jackson verwenden möchten, müssen Sie die Anmerkungen verwenden, die von @PiersyP in seiner Antwort erwähnt wurden. Sie können auch ein Builder-Muster verwenden. Wenn Sie auf einige Ausnahmen stoßen, viel Glück. Fehlerbehandlung in Jackson ist zum Kotzen, es ist schwer zu verstehen, dass Kauderwelsch in Fehlermeldungen.

Vlasec
quelle
Der Grund, warum Sie einen "Standard-Konstruktor ohne Argumente" für FooClass () benötigen, liegt wahrscheinlich darin, dass Spring der JavaBean-Spezifikation folgt, die dies erfordert, um beim Serialisieren und Deserialisieren von Objekten automatisch Marshalling und Unmarshalling durchzuführen.
Atom88
Nun, Java-Serialisierung und Deserialisierung in Binärdatenströme sind sowieso nicht das, worum es geht. Es ist also nur gut, dass Jackson mehrere Muster für die Deserialisierung anbietet. Ich mag besonders das Builder-Muster, da es ermöglicht, dass das resultierende Objekt unveränderlich ist.
Vlasec
2

In Bezug auf die letzte Veröffentlichung hatte ich das gleiche Problem, bei dem die Verwendung von Lombok 1.18. * Das Problem verursachte.

Meine Lösung bestand darin, @NoArgsConstructor (Konstruktor ohne Parameter) hinzuzufügen, da @Data standardmäßig @RequiredArgsConstructor (Konstruktor mit Parametern) enthält.

lombok-Dokumentation https://projectlombok.org/features/all

Das würde das Problem lösen:

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
quelle
0

Fehlerhafte benutzerdefinierte Jackson-Serializer / Deserializer könnten ebenfalls das Problem sein. Obwohl es nicht Ihr Fall ist, ist es erwähnenswert.

Ich hatte die gleiche Ausnahme und das war der Fall.

Artem Novikov
quelle
0

Früher funktionierte dies, aber durch das Aktualisieren von Bibliotheken trat dieses Problem auf. Das Problem war, eine Klasse wie diese zu haben:

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

Mit Lombok:

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

Zurückfallen auf

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

Das Problem wurde behoben. Ich weiß nicht warum, wollte es aber für die Zukunft dokumentieren.

eis
quelle