web-dev-qa-db-fra.com

Comment mapper une valeur imbriquée sur une propriété à l'aide des annotations de Jackson?

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.

72
kenske

Vous pouvez réaliser ceci comme ça:

String brandName;

@JsonProperty("brand")
private void unpackNameFromNestedObject(Map<String, String> brand) {
    brandName = brand.get("name");
}
73
Jacek Grobelny

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.

6
Sunny KC

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

3
gogstad

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")));
}
0
Toseef Zafar