Zuordnen eines dynamischen JSON-Objekts mit Jackson

1. Einleitung

Die Arbeit mit vordefinierten JSON-Datenstrukturen mit Jackson ist unkompliziert. Manchmal müssen wir jedoch dynamische JSON-Objekte verarbeiten, die unbekannte Eigenschaften haben .

In diesem kurzen Tutorial werden verschiedene Möglichkeiten zum Zuordnen dynamischer JSON-Objekte zu Java-Klassen vorgestellt.

Beachten Sie, dass wir in allen Tests davon ausgehen, dass wir ein Feld objectMapper vom Typ com.fasterxml.jackson.databind.ObjectMapper haben .

2. Verwenden von JsonNode

Angenommen, wir möchten Produktspezifikationen in einem Webshop verarbeiten. Alle Produkte haben einige gemeinsame Eigenschaften, aber es gibt andere, die von der Art des Produkts abhängen.

Zum Beispiel möchten wir das Seitenverhältnis der Anzeige eines Mobiltelefons wissen, aber diese Eigenschaft macht für einen Schuh wenig Sinn.

Die Datenstruktur sieht folgendermaßen aus:

{ "name": "Pear yPhone 72", "category": "cellphone", "details": { "displayAspectRatio": "97:3", "audioConnector": "none" } }

Wir speichern die dynamischen Eigenschaften im Detailobjekt .

Wir können die allgemeinen Eigenschaften mit der folgenden Java-Klasse zuordnen:

class Product { String name; String category; // standard getters and setters }

Darüber hinaus benötigen wir eine entsprechende Darstellung für das Detailobjekt . Beispielsweise kann com.fasterxml.jackson.databind.JsonNode dynamische Schlüssel verarbeiten .

Um es zu verwenden, müssen wir es unserer Produktklasse als Feld hinzufügen :

class Product { // common fields JsonNode details; // standard getters and setters }

Schließlich überprüfen wir, ob es funktioniert:

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector").asText()).isEqualTo("none");

Wir haben jedoch ein Problem mit dieser Lösung. Unsere Klasse hängt von der Jackson-Bibliothek ab, da wir ein JsonNode- Feld haben.

3. Verwenden der Karte

Wir können dieses Problem lösen, indem wir java.util.Map für das Detailfeld verwenden. Genauer gesagt müssen wir Map verwenden .

Alles andere kann gleich bleiben:

class Product { // common fields Map details; // standard getters and setters }

Und dann können wir es mit einem Test überprüfen:

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

4. Verwenden von @JsonAnySetter

Die vorherigen Lösungen sind gut, wenn ein Objekt nur dynamische Eigenschaften enthält. Manchmal haben wir jedoch feste und dynamische Eigenschaften, die in einem einzelnen JSON-Objekt gemischt sind .

Beispielsweise müssen wir möglicherweise unsere Produktdarstellung reduzieren:

{ "name": "Pear yPhone 72", "category": "cellphone", "displayAspectRatio": "97:3", "audioConnector": "none" }

Wir können eine solche Struktur als dynamisches Objekt behandeln. Das bedeutet leider, dass wir keine gemeinsamen Eigenschaften definieren können - wir müssen sie auch dynamisch behandeln.

Alternativ können wir @JsonAnySetter verwenden , um eine Methode zum Behandeln zusätzlicher, unbekannter Eigenschaften zu markieren . Eine solche Methode sollte zwei Argumente akzeptieren: den Namen und den Wert der Eigenschaft:

class Product { // common fields Map details = new LinkedHashMap(); @JsonAnySetter void setDetail(String key, Object value) { details.put(key, value); } // standard getters and setters }

Beachten Sie , dass wir die instanziiert haben Details Objekt zu vermeiden Nullpointerexceptions .

Da wir die dynamischen Eigenschaften in einer Karte speichern , können wir sie wie zuvor verwenden:

String json = ""; Product product = objectMapper.readValue(json, Product.class); assertThat(product.getName()).isEqualTo("Pear yPhone 72"); assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");

5. Erstellen eines benutzerdefinierten Deserializers

In den meisten Fällen funktionieren diese Lösungen einwandfrei. Manchmal brauchen wir jedoch viel mehr Kontrolle. Beispielsweise könnten wir Deserialisierungsinformationen zu unseren JSON-Objekten in einer Datenbank speichern.

Wir können diese Situationen mit einem benutzerdefinierten Deserializer angehen. Da es sich um ein komplexes Thema handelt, behandeln wir es in einem anderen Artikel, der mit der benutzerdefinierten Deserialisierung in Jackson beginnt.

6. Fazit

In diesem Artikel haben wir mehrere Möglichkeiten zum Umgang mit dynamischen JSON-Objekten mit Jackson gesehen.

Wie üblich sind die Beispiele auf GitHub verfügbar.