JsonMappingException: Tidak ditemukan konstruktor yang cocok untuk tipe [tipe sederhana, kelas]: tidak dapat instantiate dari objek JSON

438

Saya mendapatkan kesalahan berikut ketika mencoba untuk mendapatkan permintaan JSON dan memprosesnya:

org.codehaus.jackson.map.JsonMappingException: Tidak ditemukan konstruktor yang cocok untuk tipe [tipe sederhana, kelas com.myweb.ApplesDO]: tidak dapat instantiate dari objek JSON (perlu menambah / mengaktifkan informasi jenis?)

Inilah JSON yang saya coba kirim:

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

Di Kontroler, saya memiliki tanda tangan metode berikut:

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

AllApplesDO adalah pembungkus ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

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

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

ApelDO:

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

Saya berpikir bahwa Jackson tidak dapat mengubah JSON menjadi objek Java untuk subkelas. Tolong bantu dengan parameter konfigurasi untuk Jackson untuk mengkonversi JSON menjadi Java Objects. Saya menggunakan Spring Framework.

EDIT: Termasuk bug utama yang menyebabkan masalah ini di kelas sampel di atas - Silakan lihat jawaban yang diterima untuk solusi.

Lucky Murari
sumber
2
Saya tidak melihat subclass dalam kode di atas, apakah kode ini yang Anda coba atau Anda membuat contoh sederhana?
gkamal
Saya menambahkan jawaban dengan penjelasan lebih lanjut tentang cara kerjanya. Pada dasarnya, Anda perlu menyadari bahwa Java tidak menyimpan nama argumen metode dalam runtime.
Vlasec

Jawaban:

565

Jadi, akhirnya saya menyadari apa masalahnya. Ini bukan masalah konfigurasi Jackson karena saya ragu.

Sebenarnya masalahnya ada di ApplesDO Class:

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

Ada konstruktor khusus yang ditentukan untuk kelas menjadikannya konstruktor default. Memperkenalkan konstruktor dummy telah membuat kesalahan hilang:

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
sumber
Bolehkah saya bertanya dari mana asal CustomType. Saya mencoba struktur seperti ini tetapi saya benar-benar baru di Jawa.
andho
181
Anda dapat menggunakan jackson dengan kelas dalam (bersarang), serialisasi berfungsi dengan baik dalam kasus ini. Satu-satunya batu sandungan adalah bahwa kelas batin harus ditandai sebagai "statis" agar deserialisasi berfungsi dengan baik. Lihat penjelasan di sini: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell
3
Bisakah seseorang tolong jelaskan mengapa ini terjadi? Saya memiliki kesalahan yang sangat mirip. Pikir semua konstruktor yang tepat sudah ada, tetapi tidak bisa deserialize. Ini hanya berfungsi setelah saya menambahkan konstruktor boneka, setelah membaca posting ini.
user8658912
6
@ Manusia saya tidak akan menyebut ini konstruktor dummy - itu hanya konstruktor default. Tidak hanya itu benar-benar valid, tetapi diperlukan untuk banyak jenis pengolahan kacang java. (Dan, tentu saja, itu membuatku tersandung. :-))
fool4jesus
2
Jika Anda tidak ingin menambahkan konstruktor default (misalnya, ketika Anda berhadapan dengan objek yang tidak dapat diubah). Anda harus memberi tahu konstruktor atau metode pabrik mana yang digunakan untuk membuat instance objek menggunakan anotasi JsonCreator.
Rahul
376

Ini terjadi karena alasan berikut:

  1. kelas batin Anda harus didefinisikan sebagai statis

    private static class Condition {  //jackson specific    
    }
  2. Mungkin Anda tidak mendapat konstruktor default di kelas Anda ( PEMBARUAN: Tampaknya tidak demikian)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Bisa jadi setter Anda tidak didefinisikan dengan benar atau tidak terlihat (mis. Setter pribadi)

azerafati
sumber
95
kelas statis membuat perbedaan dalam kasus saya. Terima kasih!
jalogar
3
Dan tentu saja, Anda tidak perlu mendeklarasikan konstruktor default kosong, tanpa argumen, Java melakukannya untuk Anda ! (Selama Anda tidak mendefinisikan konstruktor lain.)
Jonik
1
@Jonik, benar! jawaban saya sudah tua, jika saya ingat benar, karena Jackson menggunakan refleksi untuk sampai ke kelas dalam, saya kira itu diperlukan untuk mendefinisikan konstruktor default (bisa juga bukan kasus dalam versi yang lebih baru), tetapi karena saya tidak yakin Anda bisa benar.
azerafati
6
Ya, pengalaman saya dengan Jackson 2.4.4: memang konstruktor default implisit Java sudah cukup. Anda perlu secara eksplisit menulis konstruktor no-args hanya jika Anda telah mendefinisikan konstruktor lain yang mengambil argumen (yaitu ketika Java tidak menghasilkan argumen no-arg untuk Anda).
Jonik
2
Juga dengan saya, kelas statis menyelamatkan hari itu.
Simon
58

Saya ingin menambahkan solusi lain untuk ini yang tidak memerlukan konstruktor dummy. Karena dummy constructor agak berantakan dan selanjutnya membingungkan. Kami dapat menyediakan konstruktor yang aman dan dengan menganotasi argumen konstruktor, kami mengizinkan jackson untuk menentukan pemetaan antara parameter konstruktor dan bidang.

jadi berikut ini juga akan berfungsi. Perhatikan string di dalam anotasi harus cocok dengan nama bidang.

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
sumber
Solusi ini berhasil. Saya hanya ingin menyebutkan yang berikut ini, saya memiliki konstruktor, tetapi nama parameternya berbeda dengan parameter instance, jadi tidak bisa dipetakan. Menambahkan anotasi menyelesaikannya, tetapi mengubah nama parameter mungkin akan bekerja juga.
Fico
Bagaimana jika parameter konstruktor tidak ada dalam respons? Apakah bisa disuntikkan dengan cara lain?
Eddie Jaoude
Satu-satunya data penting yang dikirim ke sini adalah String apple yang merupakan responsnya.
PiersyP
1
Ini berguna bagi saya karena saya ingin objek saya tidak berubah, jadi konstruktor dummy bukanlah pilihan.
Jonathan Pullano
Dalam kasus saya juga perlu menambahkan @JsonCreatorkonstruktor dengan @JsonProperty.
m1ld
31

Ketika saya mengalami masalah ini, itu adalah hasil dari mencoba menggunakan kelas batin untuk berfungsi sebagai DO. Konstruksi kelas dalam (diam-diam) membutuhkan turunan dari kelas penutup - yang tidak tersedia untuk Jackson.

Dalam hal ini, memindahkan kelas dalam ke file .java sendiri memperbaiki masalah.

tanda
sumber
6
Sedangkan memindahkan kelas dalam ke file .java sendiri berfungsi, menambahkan pengubah statis juga mengatasi masalah seperti yang disebutkan dalam jawaban @ bludream.
Penanda
20

Secara umum kesalahan ini terjadi karena kita tidak membuat konstruktor default tetapi dalam kasus saya Masalahnya datang hanya karena saya telah membuat kelas objek yang digunakan di dalam kelas induk. Ini telah menghabiskan seluruh hari saya.

alok
sumber
Sudah cukup membuat kelas bersarang static.
sloth
13

Aturan Jempol : Tambahkan konstruktor default untuk setiap kelas yang Anda gunakan sebagai kelas pemetaan. Anda melewatkan ini dan masalah muncul!
Cukup tambahkan konstruktor default dan itu akan berfungsi.

Badal
sumber
jawaban yang bagus. Terima kasih. Menyimpan hariku.
Steve
9

Bisakah Anda menguji struktur ini. Jika saya ingat benar Anda dapat menggunakannya dengan cara ini:

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

Kedua, tolong tambahkan konstruktor default untuk setiap kelas yang mungkin juga membantu.

danny.lesnik
sumber
Tidak berfungsi: Mendapatkan kesalahan berikut: "org.codehaus.jackson.map.exc.Unggak DiakuiPropertyException: Bidang yang tidak dikenali & quot; applesRequest & quot; (Kelas com.smartshop.dao.AllApplesDO), tidak ditandai sebagai dapat diabaikan"
Lucky Murari
Sebelumnya dulu setidaknya tidak melalui kesalahan untuk AllApplesDO dan hanya melempar untuk kelas tertutup .. sekarang melempar untuk kelas pertama itu sendiri
Lucky Murari
Diperlukan konstruktor default. Terima kasih!
Planky
bukankah ini harus dipilih sebagai jawaban yang BENAR?
gigi cepat
7

Anda harus membuat konstruktor kosong dummy di kelas model kami. Jadi saat memetakan json, itu ditetapkan dengan metode setter.

Suresh Patil
sumber
Ini perbaikannya.
David Kobia
Itu juga yang terjadi pada saya. Saya memiliki banyak konstruktor berbeda untuk objek saya, jadi saya baru saja membuat yang kosong yang tampaknya digunakan oleh jackson.
Alessandro Roaro
5

Jika Anda memulai anotasi konstruktor, Anda harus memberi anotasi pada semua bidang.

Perhatikan, bidang Staff.name saya dipetakan ke "ANOTHER_NAME" dalam string 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;
       }        
    }
Pusaran
sumber
4

Anda harus menyadari opsi apa yang tersedia untuk deserialisasi. Di Jawa, nama argumen metode tidak ada dalam kode yang dikompilasi. Itulah sebabnya Jackson umumnya tidak dapat menggunakan konstruktor untuk membuat objek yang terdefinisi dengan baik dengan semua yang telah ditetapkan.

Jadi, jika ada konstruktor kosong dan ada juga setter, itu menggunakan konstruktor kosong dan setter. Jika tidak ada setter, beberapa sihir gelap (refleksi) digunakan untuk melakukannya.

Jika Anda ingin menggunakan konstruktor dengan Jackson, Anda harus menggunakan anotasi seperti yang disebutkan oleh @PiersyP dalam jawabannya. Anda juga dapat menggunakan pola pembangun. Jika Anda menemukan beberapa pengecualian, semoga berhasil. Menangani kesalahan di Jackson sangat menyebalkan, sulit untuk memahami bahwa omong kosong dalam pesan kesalahan.

Vlasec
sumber
Alasan Anda memerlukan konstruktor "default tanpa argumen" FooClass () kemungkinan karena Spring mengikuti Spesifikasi JavaBean, yang mengharuskan ini berfungsi untuk membuat dan menghapus secara otomatis ketika membuat objek serial dan meng-deserialisasi.
atom88
Yah, serialisasi Java dan deserialisasi ke dalam aliran biner bukan tentang pertanyaannya. Jadi, hanya baik kalau Jackson menawarkan banyak pola untuk digunakan dengan deserialisasi. Saya terutama menyukai pola pembangun karena memungkinkan objek yang dihasilkan menjadi tidak berubah.
Vlasec
3

Mengenai publikasi terakhir saya punya masalah yang sama di mana menggunakan Lombok 1.18. * Menghasilkan masalah.

Solusi saya adalah menambahkan @NoArgsConstructor (konstruktor tanpa parameter), karena @ Data menyertakan secara default @RequiredArgsConstructor (Konstruktor dengan parameter).

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

Itu akan memecahkan masalah:

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

Gagal kustom jackson Serializers / Deserializers juga bisa menjadi masalah. Meskipun ini bukan kasus Anda, perlu disebutkan.

Saya menghadapi pengecualian yang sama dan itulah yang terjadi.

Artem Novikov
sumber
0

Bagi saya, ini dulu berfungsi, tetapi memutakhirkan perpustakaan menyebabkan masalah ini muncul. Masalahnya memiliki kelas seperti ini:

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

Menggunakan lombok:

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

Jatuh kembali ke

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

Memperbaiki masalah. Tidak yakin mengapa, tetapi ingin mendokumentasikannya untuk masa depan.

eis
sumber