Wie kann man einen verschachtelten Wert mithilfe von Jackson-Anmerkungen einer Eigenschaft zuordnen?

Lesezeit: 6 Minuten

Benutzer-Avatar
kenske

Angenommen, ich rufe eine API auf, die mit dem folgenden JSON für ein Produkt antwortet:

{
  "id": 123,
  "name": "The Best Product",
  "brand": {
     "id": 234,
     "name": "ACME Products"
  }
}

Ich kann die Produkt-ID und den Namen mithilfe von Jackson-Anmerkungen problemlos zuordnen:

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;
    }
}

Und dann verwenden Sie die fromJson-Methode, um das Produkt zu erstellen:

  JsonNode apiResponse = api.getResponse();
  Product product = Json.fromJson(apiResponse, Product.class);

Aber jetzt versuche ich herauszufinden, wie ich an den Markennamen komme, der eine verschachtelte Eigenschaft ist. Ich hatte gehofft, dass so etwas funktioniert:

    @JsonProperty("brand.name")
    public String getBrandName() {
        return brandName;
    }

Aber natürlich nicht. Gibt es eine einfache Möglichkeit, mit Anmerkungen das zu erreichen, was ich möchte?

Die eigentliche JSON-Antwort, die ich zu analysieren versuche, ist sehr komplex, und ich möchte nicht für jeden Unterknoten eine komplett neue Klasse erstellen müssen, obwohl ich nur ein einziges Feld benötige.

  • Am Ende habe ich verwendet github.com/json-path/JsonPath — Spring verwendet es auch unter der Haube. Zum Beispiel in ihrem org.springframework.data.web.

    – Entmenschlicher

    6. November 2017 um 22:21 Uhr

Das kannst du so erreichen:

String brandName;

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

  • Wie wäre es mit drei Ebenen tief?

    – Robin Kanters

    18. Januar 2017 um 21:59 Uhr

  • @ Robin Würde das nicht funktionieren? this.abbreviation = ((Map<String, Object>)portalCharacteristics.get("icon")).get("ticker").toString();

    – Marcello de Sales

    31. August 2017 um 6:11 Uhr

  • Dieselbe Methode kann auch zum Entpacken mehrerer Werte verwendet werden … unpackValuesFromNestedBrand – danke

    – msanjay

    20. August 2018 um 9:07 Uhr

  • Verwenden Sie für drei Ebenen direkt JsonNode. Karte wird zu umständlich: void unpackName(JsonNode company) { name = company.get("division").get("brand").get("name).asText(); }

    – Michał Grzejszczak

    26. Januar 2021 um 11:00 Uhr


  • @MichałGrzejszczak – Was ist der Wert der JsonProperty-Anmerkung, wenn JsonNode für mehrstufige Zuordnungen verwendet wird?

    – Andy Dufresne

    8. Februar um 10:03 Uhr

So habe ich dieses Problem gelöst:

Brand Klasse:

package org.answer.entity;

public class Brand {

    private Long id;

    private String name;

    public Brand() {

    }

    //accessors and mutators
}

Product Klasse:

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
}

Hier die brand Instanz wird von ignoriert Jackson während serialization und deserializationda es mit annotiert ist @JsonIgnore.

Jackson verwendet die mit kommentierte Methode @JsonGetter zum serialization von Java-Objekt in JSON Format. Also, die brandName ist mit eingestellt brand.getName().

Ähnlich, Jackson verwendet die mit kommentierte Methode @JsonSetter zum deserialization von JSON in Java-Objekt formatieren. In diesem Szenario müssen Sie die instanziieren brand Objekt selbst und setze seine name Eigentum aus brandName.

Sie können verwenden @Transient Persistenzanmerkung mit brandNamewenn Sie möchten, dass es vom Persistenzanbieter ignoriert wird.

Sie können JsonPath-Ausdrücke verwenden, um verschachtelte Eigenschaften zuzuordnen. Ich glaube nicht, dass es irgendeine offizielle Unterstützung gibt (siehe Dies Problem), aber hier gibt es eine inoffizielle Implementierung: https://github.com/elasticpath/json-unmarshaller

Am besten verwenden Sie Setter-Methoden:

JSON:

...
 "coordinates": {
               "lat": 34.018721,
               "lng": -118.489090
             }
...

Die Setter-Methode für Lat oder Lng sieht folgendermaßen aus:

 @JsonProperty("coordinates")
    public void setLng(Map<String, String> coordinates) {
        this.lng = (Float.parseFloat(coordinates.get("lng")));
 }

Wenn Sie beide lesen müssen (wie Sie es normalerweise tun würden), verwenden Sie eine benutzerdefinierte Methode

@JsonProperty("coordinates")
public void setLatLng(Map<String, String> coordinates){
    this.lat = (Float.parseFloat(coordinates.get("lat")));
    this.lng = (Float.parseFloat(coordinates.get("lng")));
}

Dies ist meine Lösung für irgendwelche lösen Problem im Zusammenhang mit der verschachtelten JSON-Zuordnung.

Speichern Sie Ihre Antwort zunächst als Karte:

        ResponseEntity<Map<String, Object>> response =
        restTemplate.exchange(
            requestUri,
            HttpMethod.GET,
            buildAuthHeader(),
            new ParameterizedTypeReference<>() {
            });

Jetzt können Sie lesen einen beliebigen verschachtelten Wert in Ihrem JSON-Baum. Zum Beispiel:

response.getBody().get("content").get(0).get("terminal")

In diesem Fall lautet mein JSON:

{ "page": 0, "total_pages": 1, "total_elements": 2, "content": [
    {
        "merchant": "000405",
        "terminal": "38010101",
        "status": "Para ser instalada", 
....

  • That cant work without casting to Map<String, Object> after each get("node")

    – Mistriel

    Feb 22 at 19:40

  • That is in case you need to store the whole map in a variable. If you call .get("terminal") inline, you just get the value in string “38010101”

    – julianm

    Feb 23 at 16:43


user avatar
TheJeff

Just use @JsonUnwrapped:

https://www.logicbig.com/tutorials/misc/jackson/json-unwrapped.html

This puts the child objects in the parent.

  • That cant work without casting to Map<String, Object> after each get("node")

    – Mistriel

    Feb 22 at 19:40

  • That is in case you need to store the whole map in a variable. If you call .get("terminal") inline, you just get the value in string “38010101”

    – julianm

    Feb 23 at 16:43


user avatar
Srikanth Lankapalli

To make it simple ..I have written the code …most of it is self explanatory.

Main Method

package com.test;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class LOGIC {

    public static void main(String[] args) wirft JsonParseException, JsonMappingException, IOException { ObjectMapper objectMapper = new ObjectMapper();  String DATA = "{\r\n" + " \"id\": 123,\r\n" + " \"name\": \"Das beste Produkt\",\r\n" + " \" brand\": {\r\n" + " \"id\": 234,\r\n" + " \"name\": \"ACME-Produkte\"\r\n" + " }\r\ n" + "}";  ProductTest productTest = objectMapper.readValue(DATA, ProductTest.class);  System.out.println (productTest.toString());  } }

Class ProductTest

package com.test;

import com.fasterxml.jackson.annotation.JsonProperty;

public class ProductTest {

    private int productId;
    private String productName;
    private BrandName 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;
    }

    @JsonProperty("brand")
    public BrandName getBrandName() {
        return brandName;
    }
    public void setBrandName(BrandName brandName) {
        this.brandName = brandName;
    }

    @Override
    public String toString() {
        return "ProductTest [productId=" + productId + ", productName=" + productName + ", brandName=" + brandName
                + "]";
    }



}

Class BrandName

package com.test;

public class BrandName {

    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "BrandName [id=" + id + ", name=" + name + "]";
    }




}

OUTPUT

ProductTest [productId=123, productName=The Best Product, brandName=BrandName [id=234, name=ACME Products]]

  • Das funktioniert, aber ich versuche eine Lösung zu finden, bei der ich nicht für jeden Knoten eine neue Klasse erstellen muss, auch wenn ich nur ein Feld benötige.

    – Kenske

    3. Mai 2016 um 19:48 Uhr

1311940cookie-checkWie kann man einen verschachtelten Wert mithilfe von Jackson-Anmerkungen einer Eigenschaft zuordnen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy