J'utilise FasterXML/Jackson-Databind dans mon projet depuis un moment et tout fonctionnait bien jusqu'à ce que je découvre ce post et commence à utiliser cette approche pour dessérialiser des objets sans @JsonProperty annotations.
Le problème est que quand j'ai un constructeur qui prend plusieurs paramètres et décore ce constructeur avec l'annotation @JsonCreator, Jackson renvoie l'erreur suivante:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException:
Argument #0 of constructor [constructor for com.eliti.model.Cruiser, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
at [Source: {
"class" : "com.eliti.model.Cruiser",
"inventor" : "afoaisf",
"type" : "MeansTransport",
"capacity" : 123,
"maxSpeed" : 100
}; line: 1, column: 1]
J'ai créé un petit projet } _ pour illustrer le problème. Le cours que je tente de dessérialiser est celui-ci:
public class Cruise extends WaterVehicle {
private Integer maxSpeed;
@JsonCreator
public Cruise(String name, Integer maxSpeed) {
super(name);
System.out.println("Cruise.Cruise");
this.maxSpeed = maxSpeed;
}
public Integer getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
Et le code à désérialiser est le suivant:
public class Test {
public static void main(String[] args) throws IOException {
Cruise cruise = new Cruise("asd", 100);
cruise.setMaxSpeed(100);
cruise.setCapacity(123);
cruise.setInventor("afoaisf");
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));
String cruiseJson = mapper.writeValueAsString(cruise);
System.out.println(cruiseJson);
System.out.println(mapper.readValue(cruiseJson, Cruise.class));
}
J'ai déjà essayé de supprimer @JsonCreator, mais si je le fais, le jette l'exception suivante:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.eliti.model.Cruise: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {
"class" : "com.eliti.model.Cruise",
"inventor" : "afoaisf",
"type" : "MeansTransport",
"capacity" : 123,
"maxSpeed" : 100
}; line: 3, column: 3]
J'ai essayé d'émettre une "installation propre de MVN", mais le problème persiste.
Juste pour inclure des informations supplémentaires, j'ai effectué des recherches approfondies sur ce problème (problèmes GitHub, articles de blog, StackOverflow Q & R). Voici quelques enquêtes/investigations que je faisais de mon côté:
javap -v sur le bytecode généré me donne ceci:
MethodParameters:
Name Flags
name
maxSpeed
Quand je parle de constructeur, je suppose donc que l’indicateur -parameters est en train d’être défini pour le compilateur javac.
Si je crée un constructeur avec un seul paramètre, l'objet est initialisé, mais je veux/ai besoin d'utiliser le constructeur à plusieurs paramètres.
Si j'utilise l'annotation @JsonProperty sur chaque champ, cela fonctionne également, mais pour mon projet d'origine, le temps système est trop important car j'ai beaucoup de champs dans le constructeur (et il devient très difficile de refactoriser le code avec des annotations).
La question qui reste est: Comment puis-je faire fonctionner Jackson avec plusieurs constructeurs de paramètres sans annotations?
Vous devez ajouter l'annotation @JsonProperty en spécifiant le nom de la propriété json à transmettre au constructeur lors de la création de l'objet.
public class Cruise extends WaterVehicle {
private Integer maxSpeed;
@JsonCreator
public Cruise(@JsonProperty("name") String name, @JsonProperty("maxSpeed")Integer maxSpeed) {
super(name);
System.out.println("Cruise.Cruise");
this.maxSpeed = maxSpeed;
}
public Integer getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
MODIFIER
Je viens de tester en utilisant le code ci-dessous et cela fonctionne pour moi
import Java.io.IOException;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
class WaterVehicle {
private String name;
private int capacity;
private String inventor;
public WaterVehicle(String name) {
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public String getInventor() {
return inventor;
}
public void setInventor(String inventor) {
this.inventor = inventor;
}
}
class Cruise extends WaterVehicle{
private Integer maxSpeed;
public Cruise(String name, Integer maxSpeed) {
super(name);
this.maxSpeed = maxSpeed;
}
public Integer getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
public class Test {
public static void main(String[] args) throws IOException {
Cruise cruise = new Cruise("asd", 100);
cruise.setMaxSpeed(100);
cruise.setCapacity(123);
cruise.setInventor("afoaisf");
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
mapper.registerModule(new ParameterNamesModule(Mode.PROPERTIES));
String jsonString = mapper.writeValueAsString( cruise);
System.out.println(jsonString);
Cruise anotherCruise = mapper.readValue(jsonString, Cruise.class);
System.out.println(anotherCruise );
jsonString = mapper.writeValueAsString( anotherCruise );
System.out.println(jsonString);
}
}
Il produit la sortie suivante
{
"name" : "asd",
"capacity" : 123,
"inventor" : "afoaisf",
"maxSpeed" : 100
}
Cruise@56f4468b
{
"name" : "asd",
"capacity" : 123,
"inventor" : "afoaisf",
"maxSpeed" : 100
}
Assurez-vous que compilerArgs est dans le fichier pom.
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
Réponse courte: utilisez Java 8, javac -parameters
et noms-de-paramètre-module-jackson
Réponse longue: Pourquoi quand un constructeur est annoté avec @JsonCreator, ses arguments doivent être annotés avec @JsonProperty?
@JsonCreator
n'est pas nécessaire après avoir @JsonProperty("xxx")
dans le paramètre