Verschachtelte Werte mit Jackson zuordnen

Verschachtelte Werte mit Jackson zuordnen

1. Überblick

Ein typischer Anwendungsfall bei der Arbeit mit JSON ist die Durchführung einer Transformation von einem Modell in ein anderes. Zum Beispiel möchten wir vielleicht ein komplexes, dicht verschachteltes Objektdiagramm in ein einfacheres Modell zur Verwendung in einer anderen Domäne zerlegen.

In diesem kurzen Artikel sehen wir unshow to map nested values with Jackson an, um eine komplexe Datenstruktur zu reduzieren. Wir deserialisieren JSON auf drei verschiedene Arten:

  • Verwenden von@JsonProperty

  • Verwenden vonJsonNode

  • Verwenden eines benutzerdefiniertenJsonDeserializer

Weitere Lektüre:

Optional mit Jackson verwenden

Ein kurzer Überblick darüber, wie wir die Option mit Jackson verwenden können.

Read more

Vererbung mit Jackson

In diesem Lernprogramm wird gezeigt, wie die Einbeziehung von Subtyp-Metadaten und das Ignorieren von Eigenschaften, die von Superklassen mit Jackson geerbt wurden, gehandhabt werden.

Read more

Verwenden von @JsonComponent in Spring Boot

Erfahren Sie, wie Sie die @ JsonComponent-Annotation in Spring Boot verwenden.

Read more

2. Maven-Abhängigkeit

Fügen wir zunächstpom.xml die folgende Abhängigkeit hinzu:


    com.fasterxml.jackson.core
    jackson-databind
    2.9.4

Wir können die neuesten Versionen vonjackson-databind aufMaven Central finden.

3. JSON-Quelle

Betrachten Sie das folgende JSON als Quellmaterial für unsere Beispiele. Beachten Sie, dass wir bei der Erstellung der Struktur Eigenschaften einfügen, die zwei Ebenen tief verschachtelt sind:

{
    "id": "957c43f2-fa2e-42f9-bf75-6e3d5bb6960a",
    "name": "The Best Product",
    "brand": {
        "id": "9bcd817d-0141-42e6-8f04-e5aaab0980b6",
        "name": "ACME Products",
        "owner": {
            "id": "b21a80b1-0c09-4be3-9ebd-ea3653511c13",
            "name": "Ultimate Corp, Inc."
        }
    }
}

4. Vereinfachtes Domänenmodell

In einem abgeflachten Domänenmodell, das in der folgenden KlasseProductbeschrieben ist, extrahieren wirbrandName, das eine Ebene tief in unserem Quell-JSON verschachtelt ist. Außerdem extrahieren wirownerName, das zwei Ebenen tief und innerhalb des verschachteltenbrand-Objekts verschachtelt ist:

public class Product {

    private String id;
    private String name;
    private String brandName;
    private String ownerName;

    // standard getters and setters
}

5. Zuordnung mit Anmerkungen

Um die Eigenschaft des verschachteltenbrandNamezuzuordnen, müssen wir zuerst das Objekt des verschachteltenbrandauf einMap entpacken und die Eigenschaftname extrahieren. UmownerName zuzuordnen, entpacken wir das verschachtelteowner-Objekt in einMap und extrahieren dessenname-Eigenschaft.

Wir können Jackson anweisen,unpack the nested property by using a combination of @JsonProperty and some custom logic, die wir unsererProduct-Klasse hinzufügen, hinzuzufügen:

public class Product {
    // ...

    @SuppressWarnings("unchecked")
    @JsonProperty("brand")
    private void unpackNested(Map brand) {
        this.brandName = (String)brand.get("name");
        Map owner = (Map)brand.get("owner");
        this.ownerName = owner.get("name");
    }
}

Unser Client-Code kann jetzt einObjectMapper verwenden, um unseren Quell-JSON zu transformieren, der alsString KonstanteSOURCE_JSON innerhalb der Testklasse existiert:

@Test
public void whenUsingAnnotations_thenOk() throws IOException {
    Product product = new ObjectMapper()
      .readerFor(Product.class)
      .readValue(SOURCE_JSON);

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

6. Abbildung mitJsonNode

Das Zuordnen einer verschachtelten Datenstruktur mitJsonNode erfordert etwas mehr Arbeit. Hier verwenden wirObjectMapperreadTree, um die gewünschten Felder zu analysieren:

@Test
public void whenUsingJsonNode_thenOk() throws IOException {
    JsonNode productNode = new ObjectMapper().readTree(SOURCE_JSON);

    Product product = new Product();
    product.setId(productNode.get("id").textValue());
    product.setName(productNode.get("name").textValue());
    product.setBrandName(productNode.get("brand")
      .get("name").textValue());
    product.setOwnerName(productNode.get("brand")
      .get("owner").get("name").textValue());

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

7. Zuordnung mit benutzerdefiniertenJsonDeserializer

Die Zuordnung einer verschachtelten Datenstruktur mit einem benutzerdefiniertenJsonDeserializer ist aus Sicht der Implementierung identisch mit dem Ansatz vonJsonNode. Wir erstellen zuerst dieJsonDeserializer:

public class ProductDeserializer extends StdDeserializer {

    public ProductDeserializer() {
        this(null);
    }

    public ProductDeserializer(Class vc) {
        super(vc);
    }

    @Override
    public Product deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {

        JsonNode productNode = jp.getCodec().readTree(jp);
        Product product = new Product();
        product.setId(productNode.get("id").textValue());
        product.setName(productNode.get("name").textValue());
        product.setBrandName(productNode.get("brand")
          .get("name").textValue());
        product.setOwnerName(productNode.get("brand").get("owner")
          .get("name").textValue());
        return product;
    }
}

7.1. Manuelle Registrierung des Deserializers

Um unseren benutzerdefinierten Deserializer manuell zu registrieren, muss unser Client-CodeJsonDeserializer zuModule hinzufügen,Module mitObjectMapper registrieren undreadValue: aufrufen

@Test
public void whenUsingDeserializerManuallyRegistered_thenOk()
 throws IOException {

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Product.class, new ProductDeserializer());
    mapper.registerModule(module);

    Product product = mapper.readValue(SOURCE_JSON, Product.class);

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

7.2. Automatische Registrierung des Deserializers

Alternativ zur manuellen Registrierung derJsonDeserializer, können wirregister the deserializer directly on the class:

@JsonDeserialize(using = ProductDeserializer.class)
public class Product {
    // ...
}

Bei diesem Ansatz ist keine manuelle Registrierung erforderlich. Schauen wir uns unseren Kundencode mithilfe der automatischen Registrierung an:

@Test
public void whenUsingDeserializerAutoRegistered_thenOk()
  throws IOException {

    ObjectMapper mapper = new ObjectMapper();
    Product product = mapper.readValue(SOURCE_JSON, Product.class);

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

8. Fazit

In diesem Tutorial haben wir verschiedene Möglichkeiten zur Verwendung vonJackson to parse JSON containing nested values demonstriert. Weitere Beispiele finden Sie auf unserer HauptseiteJackson Tutorial.

Und wie immer können Code-Schnipselover on GitHub gefunden werden.