Supposons que je passe un appel à une API qui répond avec le code JSON suivant pour un produit:
{
"id": 123,
"name": "The Best Product",
"brand": {
"id": 234,
"name": "ACME Products"
}
}
Je suis en mesure de mapper parfaitement l'identifiant et le nom du produit à l'aide des annotations de Jackson:
public class ProductTest {
private int productId;
private String productName, brandName;
@JsonProperty("id")
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
@JsonProperty("name")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
}
Et ensuite, en utilisant la méthode fromJson pour créer le produit:
JsonNode apiResponse = api.getResponse();
Product product = Json.fromJson(apiResponse, Product.class);
Mais maintenant, j'essaie de comprendre comment saisir le nom de marque, qui est une propriété imbriquée. J'espérais que quelque chose comme ça marcherait:
@JsonProperty("brand.name")
public String getBrandName() {
return brandName;
}
Mais bien sûr que non. Existe-t-il un moyen simple de réaliser ce que je veux en utilisant des annotations?
La réponse JSON que j'essaie d'analyser est très complexe et je ne souhaite pas créer une nouvelle classe pour chaque sous-nœud, même si je n'ai besoin que d'un seul champ.
Vous pouvez réaliser ceci comme ça:
String brandName;
@JsonProperty("brand")
private void unpackNameFromNestedObject(Map<String, String> brand) {
brandName = brand.get("name");
}
Voici comment j'ai géré ce problème:
Brand
classe:
package org.answer.entity;
public class Brand {
private Long id;
private String name;
public Brand() {
}
//accessors and mutators
}
Product
classe:
package org.answer.entity;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSetter;
public class Product {
private Long id;
private String name;
@JsonIgnore
private Brand brand;
private String brandName;
public Product(){}
@JsonGetter("brandName")
protected String getBrandName() {
if (brand != null)
brandName = brand.getName();
return brandName;
}
@JsonSetter("brandName")
protected void setBrandName(String brandName) {
if (brandName != null) {
brand = new Brand();
brand.setName(brandName);
}
this.brandName = brandName;
}
//other accessors and mutators
}
Ici, l'instance brand
sera ignorée par Jackson
pendant serialization
et deserialization
, car elle est annotée avec @JsonIgnore
.
Jackson
utilisera la méthode annotée avec @JsonGetter
pour serialization
de Java objet au format JSON
. Ainsi, la brandName
est définie avec brand.getName()
.
De même, Jackson
utilisera la méthode annotée avec @JsonSetter
pour deserialization
du JSON
au format _ dans l'objet Java. Dans ce scénario, vous devrez instancier vous-même l'objet brand
et définir sa propriété name
à partir de brandName
.
Vous pouvez utiliser @Transient
l'annotation de persistance avec brandName
si vous souhaitez que celle-ci soit ignorée par le fournisseur de persistance.
Vous pouvez utiliser les expressions JsonPath pour mapper des propriétés imbriquées. Je ne pense pas qu'il y ait de soutien officiel (voir this numéro), mais il existe une implémentation non officielle ici: https://github.com/elasticpath/json-unmarshaller
Le mieux est d'utiliser les méthodes de définition:
JSON:
...
"coordinates": {
"lat": 34.018721,
"lng": -118.489090
}
...
méthode de définition pour lat ou lng ressemblera à ceci:
@JsonProperty("coordinates")
public void setLng(Map<String, String> coordinates) {
this.lng = (Float.parseFloat(coordinates.get("lng")));
}
si vous avez besoin de lire les deux (comme vous le feriez normalement), utilisez une méthode personnalisée
@JsonProperty("coordinates")
public void setLatLng(Map<String, String> coordinates){
this.lat = (Float.parseFloat(coordinates.get("lat")));
this.lng = (Float.parseFloat(coordinates.get("lng")));
}