Mappatura di un oggetto JSON dinamico con Jackson

1. Introduzione

Lavorare con strutture dati JSON predefinite con Jackson è semplice. Tuttavia, a volte è necessario gestire oggetti JSON dinamici , che hanno proprietà sconosciute .

In questo breve tutorial, vedremo diversi modi per mappare oggetti JSON dinamici in classi Java.

Notare che in tutti i test, assumiamo di avere un campo objectMapper di tipo com.fasterxml.jackson.databind.ObjectMapper .

2. Utilizzo di JsonNode

Supponiamo di voler elaborare le specifiche del prodotto in un negozio online. Tutti i prodotti hanno alcune proprietà comuni, ma ce ne sono altre, che dipendono dal tipo di prodotto.

Ad esempio, vogliamo conoscere le proporzioni del display di un telefono cellulare, ma questa proprietà non ha molto senso per una scarpa.

La struttura dei dati ha questo aspetto:

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

Memorizziamo le proprietà dinamiche nell'oggetto dettagli .

Possiamo mappare le proprietà comuni con la seguente classe Java:

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

Inoltre, abbiamo bisogno di una rappresentazione appropriata per l' oggetto details . Ad esempio, com.fasterxml.jackson.databind.JsonNode può gestire chiavi dinamiche .

Per usarlo, dobbiamo aggiungerlo come campo alla nostra classe Product :

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

Infine, verifichiamo che funzioni:

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

Tuttavia, abbiamo un problema con questa soluzione. La nostra classe dipende dalla libreria Jackson poiché abbiamo un campo JsonNode .

3. Utilizzo della mappa

Possiamo risolvere questo problema utilizzando java.util.Map per il campo dei dettagli . Più precisamente, dobbiamo usare Map .

Tutto il resto può rimanere lo stesso:

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

E poi possiamo verificarlo con un test:

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

4. Utilizzo di @JsonAnySetter

Le soluzioni precedenti sono valide quando un oggetto contiene solo proprietà dinamiche. Tuttavia, a volte abbiamo proprietà fisse e dinamiche mescolate in un singolo oggetto JSON .

Ad esempio, potrebbe essere necessario appiattire la rappresentazione del prodotto:

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

Possiamo trattare una struttura come questa come un oggetto dinamico. Sfortunatamente, ciò significa che non possiamo definire proprietà comuni, ma dobbiamo anche trattarle dinamicamente.

In alternativa, potremmo usare @JsonAnySetter per contrassegnare un metodo per la gestione di proprietà aggiuntive sconosciute . Un tale metodo dovrebbe accettare due argomenti: il nome e il valore della proprietà:

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

Nota che dobbiamo istanziare l' oggetto details per evitare NullPointerExceptions .

Dato che memorizziamo le proprietà dinamiche in una mappa , possiamo usarla nello stesso modo in cui facevamo prima:

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

5. Creazione di un deserializzatore personalizzato

Nella maggior parte dei casi, queste soluzioni funzionano perfettamente. Tuttavia, a volte abbiamo bisogno di molto più controllo. Ad esempio, potremmo memorizzare le informazioni di deserializzazione sui nostri oggetti JSON in un database.

Possiamo indirizzare queste situazioni con un deserializzatore personalizzato. Poiché si tratta di un argomento complesso, lo trattiamo in un articolo diverso, Introduzione alla deserializzazione personalizzata a Jackson.

6. Conclusione

In questo articolo, abbiamo visto diversi modi di gestire oggetti JSON dinamici con Jackson.

Come al solito, gli esempi sono disponibili su GitHub.