JsonMappingException: no se encontró un constructor adecuado para el tipo [tipo simple, clase]: no se puede crear una instancia del objeto JSON

438

Recibo el siguiente error cuando intento obtener una solicitud JSON y procesarla:

org.codehaus.jackson.map.JsonMappingException: No se encontró un constructor adecuado para el tipo [tipo simple, clase com.myweb.ApplesDO]: no se puede crear una instancia del objeto JSON (¿necesita agregar / habilitar información de tipo?)

Aquí está el JSON que estoy tratando de enviar:

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

En Controller, tengo la siguiente firma de método:

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

AllApplesDO es una envoltura de ApplesDO:

public class AllApplesDO {

    private List<ApplesDO> applesDO;

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

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

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

Creo que Jackson no puede convertir JSON en objetos Java para subclases. Ayude con los parámetros de configuración para que Jackson convierta JSON en objetos Java. Estoy usando Spring Framework.

EDITAR: Incluyó el error principal que está causando este problema en la clase de muestra anterior. Busque la respuesta aceptada para la solución.

Lucky Murari
fuente
2
No veo ninguna subclase en el código anterior, ¿es este código lo que estás intentando o estás inventando un ejemplo más simple?
gkamal
Agregué una respuesta con más explicaciones de cómo funciona. Básicamente, debe darse cuenta de que Java no mantiene los nombres de los argumentos del método en tiempo de ejecución.
Vlasec

Respuestas:

565

Entonces, finalmente me di cuenta de cuál es el problema. No es un problema de configuración de Jackson como dudé.

En realidad, el problema estaba en la clase 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
    }
}

Había un constructor personalizado definido para la clase que lo convertía en el constructor predeterminado. La introducción de un constructor ficticio ha hecho que el error desaparezca:

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
fuente
¿Puedo preguntar de dónde viene CustomType? Estoy intentando una estructura como esta pero soy absolutamente nuevo en Java.
andho
181
Puede usar jackson con clases internas (anidadas), la serialización funciona bien en este caso. El único escollo es que la clase interna debe marcarse como "estática" para que la deserialización funcione correctamente. Vea la explicación aquí: cowtowncoder.com/blog/archives/2010/08/entry_411.html
jpennell
3
¿Podría alguien explicar por qué sucede esto? Tuve un error muy similar. Pensé que todos los constructores correctos estaban en su lugar, pero no podían deserializarse. Solo funcionó después de agregar un constructor ficticio, después de leer esta publicación.
user8658912
66
@Suman No llamaría a esto un constructor ficticio, es solo el constructor predeterminado. No solo es perfectamente válido, sino que se requiere para muchos tipos de procesamiento de tipo Java Bean. (Y, por supuesto, me hizo tropezar :-).)
fool4jesus
2
Si no desea agregar un constructor predeterminado (por ejemplo, cuando se trata de objetos inmutables). Tendrá que decir qué constructor o método de fábrica usar para instanciar el objeto usando la anotación JsonCreator.
Rahul
376

Esto sucede por estos motivos:

  1. su clase interna debe definirse como estática

    private static class Condition {  //jackson specific    
    }
  2. Es posible que no tenga un constructor predeterminado en su clase ( ACTUALIZACIÓN: este no parece ser el caso)

    private static class Condition {
        private Long id;
    
        public Condition() {
        }
    
        // Setters and Getters
    }
  3. Podría ser que sus Setters no estén definidos correctamente o no sean visibles (por ejemplo, setter privado)

azerafati
fuente
95
La clase estática marcó la diferencia en mi caso. ¡Gracias!
jalogar
3
Y, por supuesto, no necesita declarar un constructor predeterminado vacío y sin argumentos, ¡ Java lo hace por usted ! (Siempre y cuando no defina ningún otro constructor).
Jonik
1
@ Jonik, cierto! mi respuesta es antigua, si no recuerdo mal, ya que Jackson está usando la reflexión para llegar a la clase interna, supongo que era necesario definir el constructor predeterminado (también podría no ser un caso en las versiones más recientes), pero como no estoy Seguro que podrías estar en lo correcto.
azerafati
66
Sí, mi experiencia es con Jackson 2.4.4: de hecho, el constructor predeterminado implícito de Java es suficiente. Debe escribir explícitamente el constructor sin argumentos solo si ha definido otros constructores que toman argumentos (es decir, cuando Java no genera uno sin argumentos).
Jonik
2
También conmigo, la clase estática salvó el día.
Simon
58

Me gustaría agregar otra solución a esto que no requiera un constructor ficticio. Dado que los constructores ficticios son un poco desordenados y posteriormente confusos. Podemos proporcionar un constructor seguro y al anotar los argumentos del constructor permitimos que jackson determine la asignación entre el parámetro del constructor y el campo.

así que lo siguiente también funcionará. Tenga en cuenta que la cadena dentro de la anotación debe coincidir con el nombre del 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
fuente
Esta solución hizo el truco. Solo quiero mencionar lo siguiente, tenía un constructor, pero el nombre de los parámetros era diferente al de los parámetros de instancia, por lo que no podía mapearse. Agregar la anotación lo resolvió, pero cambiar el nombre de los parámetros probablemente también habría funcionado.
Fico
¿Qué pasa si el parámetro constructor no está en la respuesta? ¿Se puede inyectar de otra manera?
Eddie Jaoude
Los únicos datos significativos que se envían aquí son la manzana String que es la respuesta.
PiersyP
1
Esto fue útil para mí porque quería que mi objeto fuera inmutable, por lo que un constructor ficticio no era una opción.
Jonathan Pullano
En mi caso, también requiere agregar @JsonCreatorel constructor con @JsonProperty.
m1ld
31

Cuando me encontré con este problema, fue el resultado de intentar usar una clase interna para servir como DO. La construcción de la clase interna (silenciosamente) requirió una instancia de la clase de cierre, que no estaba disponible para Jackson.

En este caso, mover la clase interna a su propio archivo .java solucionó el problema.

jmarks
fuente
66
Mientras que mover la clase interna a su propio archivo .java funciona, agregar el modificador estático también resuelve el problema como se menciona en la respuesta de @ bludream.
jmarks
20

En general, este error se produce porque no hacemos un constructor predeterminado, pero en mi caso, el problema se produjo solo porque hice una clase de objeto utilizada dentro de la clase principal. Esto ha desperdiciado todo mi día.

alok
fuente
Es suficiente para hacer la clase anidada static.
pereza
13

Regla general : agregue un constructor predeterminado para cada clase que utilizó como clase de mapeo. Te perdiste esto y surgió el problema!
Simplemente agregue el constructor predeterminado y debería funcionar.

Badal
fuente
Buena respuesta. Gracias. Me salvó el día.
Steve
9

¿Puedes por favor probar esta estructura? Si recuerdo correcto, puedes usarlo de esta manera:

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

En segundo lugar, agregue el constructor predeterminado a cada clase que también podría ayudar.

danny.lesnik
fuente
No funciona: aparece el siguiente error: "org.codehaus.jackson.map.exc.UnrecognizedPropertyException: campo no reconocido & quot; applesRequest & quot; (Clase com.smartshop.dao.AllApplesDO), no marcado como ignorable"
Lucky Murari,
Anteriormente solía al menos no por error para AllApplesDO y lanza solo para la clase cerrada ... ahora lanza para la primera clase en sí misma
Lucky Murari
Se necesita un constructor predeterminado. ¡Gracias!
Planky
¿No debería elegirse esto como la respuesta CORRECTA?
diente rápido
7

Debe crear un constructor vacío ficticio en nuestra clase de modelo, por lo tanto, al mapear json, se establece mediante el método setter.

Suresh Patil
fuente
Esta es la solución.
David Kobia
Ese también es el caso para mí. Tenía muchos constructores diferentes para mi objeto, por lo que acabo de crear otro vacío que aparentemente Jackson usa.
Alessandro Roaro
5

Si comienza a anotar un constructor, debe anotar todos los campos.

Observe que mi campo Staff.name está asignado a "ANOTHER_NAME" en la cadena 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
fuente
4

Debe darse cuenta de las opciones que Jackson tiene disponibles para la deserialización. En Java, los nombres de argumentos de método no están presentes en el código compilado. Es por eso que Jackson generalmente no puede usar constructores para crear un objeto bien definido con todo lo que ya está configurado.

Entonces, si hay un constructor vacío y también hay setters, usa el constructor y setters vacíos. Si no hay setters, se usa algo de magia oscura (reflejos) para hacerlo.

Si desea usar un constructor con Jackson, debe usar las anotaciones mencionadas por @PiersyP en su respuesta. También puede usar un patrón de construcción. Si encuentra algunas excepciones, buena suerte. El manejo de errores en Jackson apesta a lo grande, es difícil entender ese galimatías en los mensajes de error.

Vlasec
fuente
La razón por la que necesita un "constructor predeterminado sin argumentos" FooClass () es probable porque Spring sigue la especificación JavaBean, que requiere que esto funcione para organizar y desarmar automáticamente al serializar y deserializar objetos.
atom88
Bueno, la serialización y deserialización de Java en flujo binario no es de lo que se trata la pregunta, de todos modos. Por lo tanto, es bueno que Jackson ofrezca múltiples patrones para ser usados ​​con deserialización. Me gusta especialmente el patrón de construcción, ya que permite que el objeto resultante sea inmutable.
Vlasec
3

Con respecto a la última publicación, tuve el mismo problema cuando el uso de Lombok 1.18. * Generó el problema.

Mi solución fue agregar @NoArgsConstructor (constructor sin parámetros), ya que @Data incluye por defecto @RequiredArgsConstructor (Constructor con parámetros).

Documentación de lombok https://projectlombok.org/features/all

Eso resolvería el 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
fuente
0

Serializadores / deserializadores jackson personalizados también podrían ser el problema. Aunque no es su caso, vale la pena mencionarlo.

Me enfrenté a la misma excepción y ese fue el caso.

Artem Novikov
fuente
0

Para mí, esto solía funcionar, pero la actualización de las bibliotecas hizo que apareciera este problema. El problema era tener una clase 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 lombok:

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

Volviendo a

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

Solucionado el problema. No estoy seguro de por qué, pero quería documentarlo para el futuro.

eis
fuente