JsonMappingException: nenhum construtor adequado encontrado para o tipo [tipo simples, classe]: não é possível instanciar a partir do objeto JSON

438

Estou recebendo o seguinte erro ao tentar obter uma solicitação JSON e processá-la:

org.codehaus.jackson.map.JsonMappingException: nenhum construtor adequado encontrado para o tipo [tipo simples, classe com.myweb.ApplesDO]: não é possível instanciar a partir do objeto JSON (é necessário adicionar / ativar informações de tipo?)

Aqui está o JSON que estou tentando enviar:

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

No Controller, tenho a seguinte assinatura de método:

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

AllApplesDO é um wrapper do ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

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

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

MaçãsDO:

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

Eu acho que Jackson não consegue converter JSON em objetos Java para subclasses. Por favor, ajude com os parâmetros de configuração para Jackson converter JSON em objetos Java. Estou usando o Spring Framework.

EDIT: Incluído o principal erro que está causando esse problema na classe de amostra acima - Por favor, procure a resposta aceita para solução.

Lucky Murari
fonte
2
Não vejo nenhuma subclasse no código acima. Esse código é o que você está tentando ou está inventando um exemplo mais simples?
gkamal
Eu adicionei uma resposta com mais algumas explicações de como isso funciona. Basicamente, você precisa perceber que o Java não mantém os nomes dos argumentos dos métodos em tempo de execução.
Vlasec 9/08/16

Respostas:

565

Então, finalmente, percebi qual é o problema. Não é um problema de configuração de Jackson, como eu duvidava.

Na verdade, o problema estava na 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
    }
}

Havia um construtor personalizado definido para a classe, tornando-o o construtor padrão. A introdução de um construtor fictício cometeu o erro de desaparecer:

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
fonte
Posso perguntar de onde vem o CustomType. Estou tentando uma estrutura como essa, mas sou absolutamente novo em Java.
andho 11/01
181
Você pode usar jackson com classes internas (aninhadas); a serialização funciona bem neste caso. O único obstáculo é que a classe interna deve ser marcada como "estática" para que a desserialização funcione corretamente. Veja a explicação aqui: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell
3
Alguém poderia explicar por que isso acontece? Eu tive um erro muito semelhante. Pensei que todos os construtores certos estavam no lugar, mas não pudemos desserializar. Só funcionou depois que adicionei um construtor fictício, depois de ler este post.
user8658912
6
@ Human Eu não chamaria isso de um construtor fictício - é apenas o construtor padrão. Não é apenas perfeitamente válido, mas é necessário para muitos tipos de processamento do tipo java bean. (E, claro, ele tropeçou-me :-).)
fool4jesus
2
Se você não deseja adicionar o construtor padrão (por exemplo, quando estiver lidando com objetos imutáveis). Você precisará informar qual construtor ou método de fábrica usar para instanciar o objeto usando a anotação JsonCreator.
Rahul 14/03
375

Isso acontece por estes motivos:

  1. sua classe interna deve ser definida como estática

    private static class Condition {  //jackson specific    
    }
  2. Pode ser que você não tenha um construtor padrão em sua classe ( UPDATE: Este parece não ser o caso)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Pode ser que seus setters não estejam definidos adequadamente ou não sejam visíveis (por exemplo, setter privado)

azerafati
fonte
95
classe estática fez a diferença no meu caso. Obrigado!
jalogar
3
E, claro, você não precisa declarar um construtor padrão vazio e sem argumentos, o Java faz isso por você ! (Contanto que você não definir quaisquer outros construtores.)
Jonik
1
@ Jonik, certo! minha resposta é antiga, se bem me lembro, já que Jackson está usando a reflexão para chegar à classe interna, acho que foi necessário definir o construtor padrão (também pode não ser o caso nas versões mais recentes), mas como não sou Certifique-se de que você pode estar correto.
Azerafati
6
Sim, minha experiência é com o Jackson 2.4.4: na verdade, o construtor padrão implícito do Java é suficiente. Você precisa escrever explicitamente o construtor no-args apenas se tiver definido outros construtores que aceitam argumentos (ou seja, quando o Java não gera o no-args para você).
Jonik
2
Também comigo, a aula estática salvou o dia.
28615 Simon
58

Gostaria de adicionar outra solução a isso que não requer um construtor fictício. Como os construtores fictícios são um pouco confusos e subsequentemente confusos. Podemos fornecer um construtor seguro e, anotando os argumentos do construtor, permitimos que jackson determine o mapeamento entre o parâmetro e o campo do construtor.

então o seguinte também funcionará. Observe que a sequência dentro da anotação deve corresponder ao nome do campo.

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
fonte
Esta solução fez o truque. Eu só quero mencionar o seguinte, eu tinha um construtor, mas o nome dos parâmetros era diferente do nome dos parâmetros da instância, portanto, não poderia ser mapeado. A adição da anotação resolveu o problema, mas a renomeação dos parâmetros provavelmente funcionaria também.
Fico
E se o parâmetro construtor não estiver na resposta? Pode ser injetado de outra maneira?
Eddie Jaoude
Os únicos dados significativos que estão sendo enviados aqui são a maçã String que é a resposta.
PiersyP
1
Isso foi útil para mim porque eu queria que meu objeto fosse imutável; portanto, um construtor fictício não era uma opção.
Jonathan Pullano
No meu caso, também requer adicionar @JsonCreatoro construtor com @JsonProperty.
M1ld 27/04
31

Quando me deparei com esse problema, foi o resultado de tentar usar uma classe interna para servir como DO. A construção da classe interna (silenciosamente) exigia uma instância da classe anexa - que não estava disponível para Jackson.

Nesse caso, mover a classe interna para seu próprio arquivo .java corrigiu o problema.

observações
fonte
6
Enquanto mover a classe interna para seu próprio arquivo .java funciona, adicionar o modificador estático também soluciona o problema, conforme mencionado na resposta do @ bludream.
jmarks
20

Geralmente, esse erro ocorre porque não criamos o construtor padrão, mas, no meu caso, o problema estava ocorrendo apenas porque eu fiz a classe de objeto usada na classe pai. Isso desperdiçou meu dia inteiro.

alok
fonte
É o suficiente para fazer a classe aninhada static.
Preguiça
13

Regra de polegar : adicione um construtor padrão para cada classe que você usou como uma classe de mapeamento. Você perdeu isso e o problema surge!
Basta adicionar o construtor padrão e ele deve funcionar.

Badal
fonte
resposta agradável. Obrigado. você salvou o meu dia.
Steve
9

Você pode testar essa estrutura? Se bem me lembro, você pode usá-lo desta maneira:

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

Segundo, adicione construtor padrão a cada classe que também pode ajudar.

danny.lesnik
fonte
Não está funcionando: Obtendo o seguinte erro: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: campo não reconhecido" maçãsRequest "(classe com.smartshop.dao.AllApplesDO), não marcado como ignorável"
Lucky Murari 2/11
Anteriormente, costumava, pelo menos não através de erro, para AllApplesDO e lança somente para a classe fechada. Agora, ele lança para a primeira classe em si
Sorte Murari
Necessário um construtor padrão. Obrigado!
Planky
isso não deve ser escolhido como a resposta correta?
fast tooth
7

Você deve criar um construtor vazio simulado em nossa classe de modelo. Portanto, ao mapear json, ele é definido pelo método setter.

Suresh Patil
fonte
Essa é a correção.
David Kobia
Esse também é o meu caso. Eu tinha muitos construtores diferentes para o meu objeto, então criei outro vazio que aparentemente é usado por jackson.
Alessandro Roaro
5

Se você começar a anotar o construtor, deverá anotar todos os campos.

Observe que meu campo Staff.name é mapeado para "ANOTHER_NAME" na 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;
       }        
    }
Vórtice
fonte
4

Você deve perceber quais opções Jackson tem disponível para desserialização. Em Java, os nomes dos argumentos do método não estão presentes no código compilado. É por isso que Jackson geralmente não pode usar construtores para criar um objeto bem definido com tudo já definido.

Portanto, se houver um construtor vazio e também houver setters, ele usará o construtor e setters vazios. Se não houver levantadores, é usada alguma magia negra (reflexos).

Se você deseja usar um construtor com Jackson, deve usar as anotações mencionadas por @PiersyP em sua resposta. Você também pode usar um padrão de construtor. Se você encontrar algumas exceções, boa sorte. O tratamento de erros em Jackson é péssimo, é difícil entender essa tagarelice nas mensagens de erro.

Vlasec
fonte
O motivo pelo qual você precisa de um FooClass () "construtor de argumento sem padrão" é provável, porque o Spring segue a Especificação JavaBean, que exige que isso funcione para o empacotamento automático e o desmarcado ao serializar e desserializar objetos.
atom88
Bem, de qualquer maneira, serialização e desserialização de Java em fluxo binário não são a questão. Portanto, é bom que Jackson ofereça vários padrões para serem usados ​​com desserialização. Gosto especialmente do padrão do construtor, pois permite que o objeto resultante seja imutável.
Vlasec
3

Em relação à última publicação, tive o mesmo problema em que o uso do Lombok 1.18. * Gerou o problema.

Minha solução foi adicionar @NoArgsConstructor (construtor sem parâmetros), pois @Data inclui por padrão @RequiredArgsConstructor (construtor com parâmetros).

Documentação do lombok https://projectlombok.org/features/all

Isso resolveria o problema:

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

Falha nos serializadores / desserializadores de jackson personalizados também pode ser o problema. Embora não seja o seu caso, vale a pena mencionar.

Eu enfrentei a mesma exceção e esse foi o caso.

Artem Novikov
fonte
0

Para mim, isso costumava funcionar, mas a atualização das bibliotecas causou esse problema. O problema era ter uma classe como esta:

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

Usando o lombok:

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

Voltando a

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

Corrigido o problema. Não sei por que, mas queria documentá-lo para o futuro.

eis
fonte