La désérialisation échoue après la mise à jour.
J'ai mis à jour mon micro-service de Spring 1.5.10.RELEASE
à Spring 2.0.3.RELEASE
ainsi que la variable lombok
de 1.16.14
à 1.18.0
et jackson-datatype-jsr310
de 2.9.4
à 2.9.6
.
La chaîne JSON -
{"heading":"Validation failed","detail":"field must not be null"}
La classe -
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {
private final String heading;
private final String detail;
private String type;
}
Appel de méthode -
ErrorDetail errorDetail = asObject(jsonString, ErrorDetail.class);
La méthode utilisée pour désérialiser -
import com.fasterxml.jackson.databind.ObjectMapper;
// more imports and class defination.
private static <T> T asObject(final String str, Class<T> clazz) {
try {
return new ObjectMapper().readValue(str, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Erreur -
Java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.foo.bar.ErrorDetail` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"heading":"Validation failed","detail":"field must not be null"}"; line: 1, column: 2]
Lombok a cessé de générer @ConstructorProperties
sur les constructeurs avec la version 1.16.20 (voir changelog ), car cela risquerait de casser les applications Java 9+ utilisant des modules. Cette annotation contient les noms des paramètres du constructeur (ils sont supprimés lors de la compilation de la classe. Il s'agit donc d'une solution de contournement permettant de récupérer les noms de paramètres à l'exécution). L'annotation n'étant plus générée par défaut, Jackson ne peut pas mapper les noms de champs sur les paramètres du constructeur.
Solution 1: Utilisez un @NoArgsConstructor
et un @Setter
, mais vous perdrez l'immuabilité (si c'est important pour vous).
Mise à jour: Seulement @NoArgsConstructor
et @Getter
(sans @Setter
) peuvent également fonctionner (car INFER_PROPERTY_MUTATORS=true
). De cette manière, vous pouvez garder la classe immuable, du moins à partir de code normal (non réfléchissant).
Solution 2: Configurez lombok pour générer à nouveau les annotations, à l'aide d'un fichier lombok.config
contenant la ligne lombok.anyConstructor.addConstructorProperties = true
. (Si vous utilisez des modules, assurez-vous que Java.desktop
se trouve sur le chemin de votre module.)
Solution 3: Utilisez la prise en charge du générateur de Jackson en combinaison avec le @Builder
de lombok, comme décrit par ici .
Vous voulez désérialiser une classe qui a un champ final. vous devez donc déclarer un constructeur qui contient le champ final à désérialiser.
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {
private final String heading;
private final String detail;
private String type;
@JsonCreator
public ErrorDetail(@JsonProperty("heading") String heading, @JsonProperty("detail") String detail) {
this.heading = heading;
this.detail = detail;
}
}
et lorsque la désérialisation avec mappeur doit/ MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS définir cette propriété false .
private static <T> T asObject(final String str, Class<T> clazz) {
try {
return new ObjectMapper().configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS,false).readValue(str, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Utiliser l'annotation @Data
est à mon avis une mauvaise approche . S'il vous plaît changez @Data
en @Getting
, @Setter
, @EqualsAndHashcode
et ainsi de suite ..
et écrivez ici s'il vous plaît, si cela vous aidera.
mettre à jour
Je suggère que @Data
crée @RequiredArgsConstructor
et que ce soit constructeur avec les champs finaux, et sans private String type
;