Pourquoi lorsqu'un constructeur est annoté avec @JsonCreator, ses arguments doivent être annotés avec @JsonProperty?

109

Dans Jackson, lorsque vous annotez un constructeur avec @JsonCreator, vous devez annoter ses arguments avec @JsonProperty. Donc ce constructeur

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

devient ceci:

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

Je ne comprends pas pourquoi c'est nécessaire. Pouvez-vous expliquer?

Ori Popowski
la source

Réponses:

113

Jackson doit savoir dans quel ordre passer les champs d'un objet JSON au constructeur. Il n'est pas possible d'accéder aux noms de paramètres en Java à l'aide de la réflexion - c'est pourquoi vous devez répéter ces informations dans les annotations.

Lukasz Wiktor
la source
9
Ce n'est pas valable pour Java8
MariuszS
12
@MariuszS C'est vrai, mais cet article explique comment se débarrasser des annotations superflues à l'aide du drapeau du compilateur Java8 et d'un module Jackson. J'ai testé l'approche et cela fonctionne.
quantum
Bien sûr, ça marche comme un charme :) docs.oracle.com/javase/tutorial/reflect/member/…
MariuszS
52

Les noms de paramètres ne sont normalement pas accessibles par le code Java au moment de l'exécution (car il est supprimé par le compilateur), donc si vous voulez cette fonctionnalité, vous devez soit utiliser la fonctionnalité intégrée de Java 8, soit utiliser une bibliothèque telle que ParaNamer pour y accéder. à lui.

Donc, pour ne pas avoir à utiliser d'annotations pour les arguments du constructeur lors de l'utilisation de Jackson, vous pouvez utiliser l'un de ces 2 modules Jackson:

noms de paramètres de module jackson

Ce module vous permet d'obtenir des arguments de constructeur sans annotations lors de l'utilisation de Java 8 . Pour l'utiliser, vous devez d'abord enregistrer le module:

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

Ensuite, compilez votre code à l'aide de l'indicateur -parameters:

javac -parameters ...

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

jackson-module-paranamer

Cet autre vous demande simplement d'enregistrer le module ou de configurer une introspection d'annotation (mais pas les deux comme indiqué par les commentaires). Il vous permet d'utiliser des arguments de constructeur sans annotations sur les versions de Java antérieures à 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());

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

Rodrigo Quesada
la source
28

Il est possible d'éviter les annotations de constructeur avec jdk8 où le compilateur introduira éventuellement des métadonnées avec les noms des paramètres du constructeur. Ensuite, avec le module jackson-module-parameter-names, Jackson peut utiliser ce constructeur. Vous pouvez voir un exemple au post Jackson sans annotations

Manos Nikolaidis
la source
obsolète et déplacé vers jackson-modules-java8 / parameter-names
naXa
6

Parce que le bytecode Java ne conserve pas les noms des arguments de méthode ou de constructeur.

lcfd
la source
1
@MariuszS en effet, mais comme il s'agit d'un nouveau drapeau de compilateur (et non par défaut), Jackson devra continuer à prendre en charge son @JsonPropertyannotation
lcfd
6

On peut simplement utiliser l'annotation java.bean.ConstructorProperties - elle est beaucoup moins verbeuse et Jackson l'accepte également. Par exemple :

  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
la source
4

Lorsque je comprends cela correctement, vous remplacez le constructeur par défaut par un constructeur paramétré et devez donc décrire les clés JSON qui sont utilisées pour appeler le constructeur avec.

Smutje
la source
3

Comme précisé dans la documentation de l' annotation , l'annotation indique que le nom de l'argument est utilisé comme nom de propriété sans aucune modification, mais il peut être spécifié sur une valeur non vide pour spécifier un nom différent:

Guy Bouallet
la source
0

Il suffit de le découvrir et d'obtenir une réponse quelque part. vous pouvez utiliser l'annotation ci-dessous depuis 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
la source