JsonMappingException: не найден подходящий конструктор для типа [простой тип, класс]: невозможно создать экземпляр объекта JSON

438

Я получаю следующую ошибку при попытке получить JSON-запрос и обработать его:

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

Вот JSON, который я пытаюсь отправить:

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

В контроллере у меня есть следующая подпись метода:

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

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

Я думаю, что Джексон не может преобразовать JSON в объекты Java для подклассов. Пожалуйста, помогите с параметрами конфигурации для Джексона для преобразования JSON в объекты Java. Я использую Spring Framework.

РЕДАКТИРОВАТЬ: Включена основная ошибка, которая вызывает эту проблему в приведенном выше примере класса - Пожалуйста, посмотрите принятый ответ для решения.

Лаки Мурари
источник
2
Я не вижу подклассов в приведенном выше коде, этот код - то, что вы пытаетесь или вы делаете более простой пример?
Гкамаль
Я добавил ответ с дополнительным объяснением того, как это работает. По сути, вы должны понимать, что 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
Может кто-нибудь объяснить, почему это происходит? У меня была очень похожая ошибка. Думал, что все правильные конструкторы были на месте, но не могли десериализоваться. Это сработало только после того, как я добавил фиктивный конструктор, после прочтения этого поста.
user8658912
6
@Suman Я бы не назвал это фиктивным конструктором - это просто конструктор по умолчанию. Это не только совершенно правильно, но и требуется для многих видов обработки Java-бинов. (И, конечно, это сбило меня с
толку
2
Если вы не хотите добавлять конструктор по умолчанию (например, когда имеете дело с неизменяемыми объектами). Вам нужно будет указать, какой конструктор или метод фабрики использовать для создания объекта с помощью аннотации JsonCreator.
Рахул
376

Это происходит по следующим причинам:

  1. Ваш внутренний класс должен быть определен как статический

    private static class Condition {  //jackson specific    
    }
  2. Возможно, в вашем классе нет конструктора по умолчанию ( UPDATE: похоже, это не тот случай)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Это может быть ваши сеттеры не определены должным образом или не видны (например, частный сеттер)

azerafati
источник
95
статический класс сделал разницу в моем случае. Спасибо!
2011 г.
3
И, конечно, вам не нужно объявлять пустой конструктор по умолчанию без аргументов, Java сделает это за вас ! (Пока вы не определяете никаких других конструкторов.)
Jonik
1
@Jonik, верно! мой ответ старый, если я правильно помню, так как Джексон использует отражение, чтобы добраться до внутреннего класса, я думаю, что это было необходимо для определения конструктора по умолчанию (это может быть не так в новых версиях), но так как я не уверен, что ты мог быть прав.
Азерафати
6
Да, мой опыт работы с Jackson 2.4.4: действительно, неявного Java-конструктора по умолчанию достаточно. Вы должны явно писать конструктор без аргументов, только если вы определили другие конструкторы, которые принимают аргументы (т.е. когда Java не генерирует для вас аргумент без аргументов).
Джоник
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
источник
Это решение помогло. Я просто хочу упомянуть следующее: у меня был конструктор, но имя параметров отличалось от имени параметров экземпляра, поэтому его нельзя было отобразить. Добавление аннотации решило это, но переименование параметров, вероятно, также сработало бы.
Фицо
Что если параметр конструктора отсутствует в ответе? Это может быть введено другим способом?
Эдди Жауде
Единственные важные данные, которые отправляются сюда, это строковое яблоко, которое является ответом.
PiersyP
1
Это было полезно для меня, потому что я хотел, чтобы мой объект был неизменным, поэтому фиктивный конструктор не был опцией.
Джонатан Пуллано
В моем случае это также требует добавить @JsonCreatorконструктор с @JsonProperty.
1
31

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

В этом случае перемещение внутреннего класса в собственный файл .java устранило проблему.

jmarks
источник
6
Принимая во внимание, что перемещение внутреннего класса в его собственный файл .java работает, добавление статического модификатора также решает проблему, как упомянуто в ответе @ bludream.
Jmarks
20

Как правило, эта ошибка возникает из-за того, что мы не делаем конструктор по умолчанию, но в моем случае проблема возникла только из-за того, что я создал класс используемого объекта внутри родительского класса. Это потратило впустую весь мой день.

Alok
источник
Достаточно сделать вложенный класс static.
ленивец
13

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

Badal
источник
хороший ответ. Спасибо. спас мой день.
Стив
9

Можете ли вы проверить эту структуру? Если я правильно помню, вы можете использовать это так:

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

Во-вторых, добавьте конструктор по умолчанию для каждого класса, это также может помочь.

danny.lesnik
источник
Не работает: появляется следующая ошибка: «org.codehaus.jackson.map.exc.UnrecognizedPropertyException: нераспознанное поле« appleRequest »(класс 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 в его ответе. Вы также можете использовать шаблон строителя. Если вы столкнулись с некоторыми исключениями, удачи. Обработка ошибок в Джексоне отстой, это трудно понять, что за ошибки в сообщениях об ошибках.

Vlasec
источник
Причина, по которой вам нужен 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

Сбой пользовательских сериализаторов / десериализаторов Джексона также может быть проблемой. Хотя это не ваш случай, стоит упомянуть.

Я столкнулся с тем же исключением, и это было так.

Артем Новиков
источник
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;
}

Используя ломбок:

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

Исправлена ​​проблема. Не уверен почему, но хотел документировать это на будущее.

эйс
источник