¿Por qué cuando un constructor está anotado con @JsonCreator, sus argumentos deben anotarse con @JsonProperty?

108

En Jackson, cuando anota un constructor con @JsonCreator, debe anotar sus argumentos con @JsonProperty. Entonces este constructor

public Point(double x, double y) {
    this.x = x;
    this.y = y;
}

se convierte en esto:

@JsonCreator
public Point(@JsonProperty("x") double x, @JsonProperty("y") double y) {
    this.x = x;
    this.y = y;
}

No entiendo por qué es necesario. ¿Puede usted explicar por favor?

Ori Popowski
fuente

Respuestas:

112

Jackson tiene que saber en qué orden pasar los campos de un objeto JSON al constructor. No es posible acceder a los nombres de los parámetros en Java mediante la reflexión, por eso debe repetir esta información en las anotaciones.

Lukasz Wiktor
fuente
9
Esto no es válido para Java8
MariuszS
12
@MariuszS Eso es cierto, pero esta publicación explica cómo deshacerse de anotaciones extrañas con la ayuda del indicador del compilador Java8 y un módulo Jackson. Probé el enfoque y funciona.
Quantum
Por supuesto, funciona como un encanto :) docs.oracle.com/javase/tutorial/reflect/member/…
MariuszS
52

Normalmente, el código Java no puede acceder a los nombres de los parámetros en tiempo de ejecución (porque el compilador los deja caer), por lo que si desea esa funcionalidad, debe usar la funcionalidad incorporada de Java 8 o usar una biblioteca como ParaNamer para obtener acceso lo.

Entonces, para no tener que utilizar anotaciones para los argumentos del constructor al usar Jackson, puede hacer uso de cualquiera de estos 2 módulos de Jackson:

nombres-de-parámetros-módulo-jackson

Este módulo le permite obtener argumentos de constructor sin anotaciones cuando usa Java 8 . Para utilizarlo, primero debe registrar el módulo:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ParameterNamesModule());

Luego compila tu código usando la marca -parameters:

javac -parameters ...

Enlace: https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names

jackson-module-paranamer

Este otro simplemente requiere que registre el módulo o configure una introspección de anotación (pero no ambos como lo señalan los comentarios). Le permite utilizar argumentos de constructor sin anotaciones en versiones de Java anteriores a la 1.8 .

ObjectMapper mapper = new ObjectMapper();
// either via module
mapper.registerModule(new ParanamerModule());
// or by directly assigning annotation introspector (but not both!)
mapper.setAnnotationIntrospector(new ParanamerOnJacksonAnnotationIntrospector());

Enlace: https://github.com/FasterXML/jackson-modules-base/tree/master/paranamer

Rodrigo Quesada
fuente
6

Porque el código de bytes de Java no retiene los nombres del método o los argumentos del constructor.

lcfd
fuente
1
@MariuszS de hecho, pero dado que se trata de un nuevo (y un indicador del compilador no predeterminado), Jackson tendrá que seguir apoyando su @JsonPropertyanotación
lcfd
6

Uno puede simplemente usar la anotación java.bean.ConstructorProperties - es mucho menos detallada y Jackson también la acepta. Por ejemplo :

  import java.beans.ConstructorProperties;

  @ConstructorProperties({"answer","closed","language","interface","operation"})
  public DialogueOutput(String answer, boolean closed, String language, String anInterface, String operation) {
    this.answer = answer;
    this.closed = closed;
    this.language = language;
    this.anInterface = anInterface;
    this.operation = operation;
  }
letowianka
fuente
4

Cuando entiendo esto correctamente, reemplaza el constructor predeterminado con uno parametrizado y, por lo tanto, tiene que describir las claves JSON que se utilizan para llamar al constructor.

Smutje
fuente
3

Como se precisa en la documentación de la anotación , la anotación indica que el nombre del argumento se usa como el nombre de la propiedad sin ninguna modificación, pero se puede especificar un valor no vacío para especificar un nombre diferente:

Guy Bouallet
fuente
0

Simplemente entréguelo y obtenga una respuesta en alguna parte. puede usar la anotación a continuación desde 2.7.0

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Point {
    final private double x;
    final private double y;

    @ConstructorProperties({"x", "y"})
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}
Andrés
fuente