Por que quando um construtor é anotado com @JsonCreator, seus argumentos devem ser anotados com @JsonProperty?

108

Em Jackson, quando você anota um construtor com @JsonCreator, você deve anotar seus argumentos com @JsonProperty. Então, este construtor

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

torna-se isto:

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

Não entendo porque é necessário. Você pode explicar, por favor?

Ori Popowski
fonte

Respostas:

112

Jackson precisa saber em que ordem passar os campos de um objeto JSON para o construtor. Não é possível acessar nomes de parâmetros em Java usando reflexão - é por isso que você deve repetir essas informações nas anotações.

Lukasz Wiktor
fonte
9
Isso não é válido para Java8
MariuszS
12
@MariuszS Isso é verdade, mas este post explica como se livrar de anotações estranhas com a ajuda do sinalizador do compilador Java8 e um módulo Jackson. Eu testei a abordagem e funciona.
quantum de
Claro, funciona como um encanto :) docs.oracle.com/javase/tutorial/reflect/member/…
MariuszS
52

Nomes de parâmetros normalmente não são acessíveis pelo código Java em tempo de execução (porque são descartados pelo compilador), então se você quiser essa funcionalidade, você precisa usar a funcionalidade integrada do Java 8 ou usar uma biblioteca como ParaNamer para obter acesso para isso.

Portanto, para não ter que utilizar anotações para os argumentos do construtor ao usar Jackson, você pode fazer uso de um destes dois módulos Jackson:

jackson-module-parameter-names

Este módulo permite que você obtenha argumentos de construtor sem anotação ao usar Java 8 . Para usá-lo, você primeiro precisa registrar o módulo:

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

Em seguida, compile seu código usando a sinalização -parameters:

javac -parameters ...

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

jackson-module-paranamer

Este outro simplesmente requer que você registre o módulo ou configure uma introspecção de anotação (mas não ambos como apontado pelos comentários). Ele permite que você use argumentos de construtor sem anotação em versões de Java anteriores a 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());

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

Rodrigo Quesada
fonte
6

Porque o bytecode Java não retém os nomes dos argumentos do método ou do construtor.

lcfd
fonte
1
@MariuszS, de fato, mas como este é um novo (e sinalizador de compilador não padrão), Jackson terá que continuar a dar suporte à sua @JsonPropertyanotação
lcfd
6

Pode-se simplesmente usar a anotação java.bean.ConstructorProperties - é muito menos prolixa e Jackson também a aceita. Por exemplo :

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

Quando eu entendi isso corretamente, você substitui o construtor padrão por um parametrizado e, portanto, tem que descrever as chaves JSON que são usadas para chamar o construtor com.

Smutje
fonte
3

Conforme especificado na documentação da anotação , a anotação indica que o nome do argumento é usado como o nome da propriedade sem nenhuma modificação, mas pode ser especificado como um valor não vazio para especificar um nome diferente:

Guy Bouallet
fonte
0

Basta encontrar e obter uma resposta em algum lugar. você pode usar a anotação abaixo 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;
    }
}
Andrew
fonte