JsonMappingException: Не знайдено відповідного конструктора для типу [простий тип, клас]: не можна інстанціювати об'єкт JSON


438

Я отримую таку помилку при спробі отримати запит JSON та обробити його:

org.codehaus.jackson.map.JsonMappingException: Не знайдено відповідного конструктора для типу [простий тип, клас com.myweb.ApplesDO]: не можна інстанціювати об'єкт JSON (потрібно додати / включити інформацію про тип?)

Ось JSON, який я намагаюся надіслати:

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

У Controller у мене є такий підпис методу:

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

AllApplesDO - це обгортка ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

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

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

ЯблукаDO:

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

Я думаю, що Джексон не в змозі перетворити JSON в об'єкти Java для підкласів. Будь ласка, допоможіть з параметрами конфігурації для Джексона для перетворення JSON в Java-об’єкти. Я використовую Spring Framework.

EDIT: Включено основну помилку, яка спричиняє цю проблему, у вищевказаний клас зразків.


2
Я не бачу жодних підкласів у наведеному вище коді, чи є це кодом, який ви намагаєтесь, чи ви складаєте простіший приклад?
gkamal

Я доповнив відповідь ще деяким поясненням того, як це працює. По суті, вам потрібно зрозуміти, що Java не зберігає імена аргументів методу під час виконання.
Власек

Відповіді:


565

Отже, нарешті я зрозумів, у чому проблема. Як я сумнівався, це не проблема конфігурації Джексона.

Насправді проблема була в класі 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
    }
}

Для класу був визначений спеціальний конструктор, що робить його конструктором за замовчуванням. Представляючи фіктивний конструктор, помилка зникла:

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

}

Чи можу я запитати, звідки походить CustomType. Я намагаюся подібну структуру, але я абсолютно новачок у Java.
andho

181
Ви можете використовувати Джексона з внутрішніми (вкладеними) класами, серіалізація в цьому випадку працює чудово. Єдиним каменем спотикання є те, що внутрішній клас повинен бути позначений як "статичний", щоб дезаріалізація працювала належним чином. Див. Виклад тут: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell

3
Невже хтось може пояснити, чому це відбувається? У мене була дуже схожа помилка. Думав, що всі правильні конструктори стоять на місці, але не могли десаріалізувати. Це спрацювало лише після того, як я додав конструктор манекенів, прочитавши цю публікацію.
користувач8658912

6
@Suman Я б не назвав це манекеном конструктора - це просто конструктор за замовчуванням. Мало того, що вона абсолютно діє, але і потрібна для багатьох видів обробки бобів типу Java. (І, звичайно, мене це
спонукало

2
Якщо ви не хочете додавати конструктор за замовчуванням (наприклад, коли ви маєте справу з незмінними об'єктами). Вам доведеться сказати, який конструктор чи заводський метод використовувати для інстанціювання об'єкта за допомогою анотації JsonCreator.
Рахул

375

Це відбувається з цих причин:

  1. ваш внутрішній клас слід визначати як статичний

    private static class Condition {  //jackson specific    
    }
  2. Можливо, у вас не було конструктора за замовчуванням у вашому класі ( ОНОВЛЕННЯ: Схоже, це не так)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Це може бути, що Ваші налаштування не визначені належним чином або не видно (наприклад, приватний сетер)


95
статичний клас змінив мою справу. Дякую!
jalogar

3
І звичайно, вам не потрібно оголошувати порожній конструктор за замовчуванням без аргументів, Java робить це за вас ! (Поки ви не визначаєте жодних інших конструкторів.)
Jonik

1
@Jonik, правильно! моя відповідь стара, якщо я пам’ятаю правильну, оскільки Джексон використовує роздуми, щоб потрапити до внутрішнього класу, я думаю, це було потрібно для визначення конструктора за замовчуванням (це може бути також не випадку у нових версіях), але оскільки я не впевнений, що ти можеш бути правильним.
azerafati

6
Так, мій досвід з Jackson 2.4.4: насправді неявного конструктора за замовчуванням Java достатньо. Вам потрібно явно написати конструктор no-args, лише якщо ви визначили інші конструктори, які беруть аргументи (тобто, коли Java не генерує для вас no-args).
Jonik

2
Також зі мною статичний клас врятував день.
Саймон

58

Я хотів би додати до цього ще одне рішення, яке не потребує фіктивного конструктора. Оскільки фіктивні конструктори трохи безладні і згодом заплутані. Ми можемо забезпечити безпечний конструктор, анотуючи аргументи конструктора, дозволимо Джексону визначити відображення між параметром конструктора та полем.

тому наступне також буде працювати. Зверніть увагу, що рядок у примітці повинен відповідати імені поля.

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

1
Це було мені корисно, оскільки я хотів, щоб мій об’єкт був непорушним, тому манекен-конструктор не був варіантом.
Джонатан Пуллано

У моєму випадку також потрібно додати @JsonCreatorконструктор з @JsonProperty.
m1ld

31

Коли я зіткнувся з цією проблемою, це було наслідком спроби використовувати внутрішній клас, щоб служити DO. Побудова внутрішнього класу (мовчки) вимагала екземпляра класу, що додається, - що Джексон не був доступний.

У цьому випадку переміщення внутрішнього класу до власного .java-файла виправило проблему.


6
У той час як переміщення внутрішнього класу до власного файлу .java працює, додавання статичного модифікатора також вирішує проблему, як згадується у відповіді @ bludream.
jmarks

20

Як правило, ця помилка виникає через те, що ми не створюємо конструктор за замовчуванням, але в моєму випадку ця проблема виникає лише через те, що я використовував об'єктний клас всередині батьківського класу. Це витратив на мій цілий день.


Досить скласти вкладений клас static.
лінощі

13

Правило великого пальця : додайте конструктор за замовчуванням для кожного класу, який ви використовували як клас відображення. Ви пропустили це і виникне питання!
Просто додайте конструктор за замовчуванням, і він повинен працювати.


приємна відповідь. Дякую, ти врятував мені день.
Стів

9

Чи можете ви протестуйте цю структуру. Якщо я пам'ятаю правильно, ви можете використовувати це таким чином:

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

По-друге, додайте конструктор за замовчуванням до кожного класу, що також може допомогти.


Не працює: Отримання наступної помилки: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Нерозпізнане поле & quot; яблукаRequest & quot; (Клас com.smartshop.dao.AllApplesDO), не позначений як ігнорування"
Lucky Murari

Раніше він використовувався принаймні не через помилку для AllApplesDO і кидає лише для закритого класу .. тепер він кидає для самого першого класу
Lucky Murari

Потрібен конструктор за замовчуванням. Дякую!
Планки

чи не слід це обрати як ПРАВИЛЬНУ відповідь?
швидкий зуб

7

Ви повинні створити фіктивний порожній конструктор у нашому класі моделей. Тому під час картографування json він встановлюється методом setter.


Це виправлення.
Девід Кобія

Це теж для мене. У мене було багато різних конструкторів для мого об’єкта, і тому я просто створив ще один порожній, який, мабуть, використовує Джексон.
Алессандро Роаро

5

Якщо ви запускаєте конструктор анотування, ви повинні коментувати всі поля.

Зауважте, моє поле Staff.name відображено в "ANOTHER_NAME" у рядку 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;
       }        
    }

4

Ви повинні усвідомити, які варіанти Джексон доступні для десеріалізації. У Java імена аргументів методу відсутні в складеному коді. Ось чому Джексон взагалі не може використовувати конструктори для створення чітко визначеного об'єкта з усім уже встановленим.

Отже, якщо є порожній конструктор і є також сетери, він використовує порожній конструктор і сеттери. Якщо сетерів немає, для цього використовується якась темна магія (відображення).

Якщо ви хочете використовувати конструктор з Джексоном, ви повинні використовувати анотації, як згадував @PiersyP у своїй відповіді. Ви також можете використовувати шаблон для побудови. Якщо ви зіткнулися з деякими винятками, удачі. Помилка в роботі Джексона відсмоктує великі терміни, важко зрозуміти, що хитрість у повідомленнях про помилки.


Причина, що вам потрібен "конструктор аргументів без аргументів" FooClass (), ймовірно, тому, що Spring дотримується специфікації JavaBean, яка вимагає, щоб це працювало для автоматичного маршалування та демарширування під час серіалізації та десеріалізації об'єктів.
atom88

Ну, серіалізація Java та десеріалізація у бінарний потік - це не про що. Тож добре лише те, що Джексон пропонує кілька моделей, які можна використовувати при десеріалізації. Особливо мені подобається візерунок будівельника, оскільки він дозволяє отриманому об'єкту бути незмінним.
Власек

3

Щодо останньої публікації у мене була та сама проблема, коли використання Lombok 1.18. * Породжувало проблему.

Моє рішення було додати @NoArgsConstructor (конструктор без параметрів), оскільки @Data за замовчуванням включає @RequiredArgsConstructor (Конструктор з параметрами).

Документація lombok https://projectlombok.org/features/all

Це вирішило б проблему:

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

0

Помилка власних серіалізаторів Jackson / Deserializer також може бути проблемою. Хоча це не ваш випадок, варто згадати.

Я зіткнувся з тим же винятком, і це було так.


0

Для мене це працювало, але оновлення бібліотек спричинило появу цієї проблеми. Проблема полягала в такому занятті:

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

Використання lombok:

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

Падіння назад до

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

Виправлена ​​проблема. Не впевнений чому, але хотів документувати це на майбутнє.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.