W3docs

Java JSON con Jackson

Analizza, genera e associa JSON in Java con la libreria Jackson — ObjectMapper e data binding.

Jackson è la libreria JSON standard de facto per Java. Il JDK non include alcuna API JSON propria, quindi quasi ogni applicazione Spring, Quarkus o Micronaut ricorre a Jackson per convertire oggetti Java in testo JSON e viceversa. Il suo punto di ingresso è una singola classe versatile — ObjectMapper — che gestisce i due compiti sempre necessari: la serializzazione (oggetto Java → JSON) e la deserializzazione (JSON → oggetto Java).

Se sei alle prime armi con JSON in Java, inizia dall'introduzione a JSON; per una libreria alternativa più leggera, consulta il capitolo su Gson. Questa pagina tratta l'aggiunta di Jackson, i tre livelli della sua API, il data binding, il modello ad albero e le annotazioni che controllano la mappatura.

Aggiungere Jackson al progetto

Jackson non fa parte del JDK, quindi va dichiarato come dipendenza. L'artefatto jackson-databind include transitivamente gli altri due moduli principali (jackson-core e jackson-annotations).

<!-- Maven -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.1</version>
</dependency>
// Gradle
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'

I tre modi di lavorare con JSON

Jackson espone gli stessi dati in tre modi diversi. Scegli il livello più adatto al compito:

ApproccioTipo principaleUsalo quando
Data bindingObjectMapper.readValue / writeValueHai un POJO o un record che rispecchia il JSON — il caso più comune
Modello ad alberoJsonNode tramite readTreeLa struttura è dinamica o hai bisogno solo di alcuni campi
StreamingJsonParser / JsonGeneratorDocumenti molto grandi che non possono essere tenuti interamente in memoria

Il data binding è quello che usi nel 90% dei casi; gli altri due sono vie di uscita.

Data binding: oggetti in input, JSON in output

ObjectMapper.writeValueAsString serializza qualsiasi oggetto Java leggendo i suoi getter (o i componenti del record); readValue fa il contrario, abbinando i nomi dei campi JSON ai tuoi campi. Un record è il tipo di destinazione più pulito — Jackson 2.12+ si associa al suo costruttore canonico automaticamente, quindi non è necessario scrivere getter né un costruttore senza argomenti.

import com.fasterxml.jackson.databind.ObjectMapper;

record User(String name, int age, boolean active) {}

ObjectMapper mapper = new ObjectMapper();

// serialize: Java -> JSON
User ada = new User("Ada", 36, true);
String json = mapper.writeValueAsString(ada);
// {"name":"Ada","age":36,"active":true}

// deserialize: JSON -> Java
User back = mapper.readValue(json, User.class);
System.out.println(back.name()); // Ada

Per le collezioni e i generici, passa a Jackson un TypeReference in modo che il tipo degli elementi sopravviva all'erasure:

import com.fasterxml.jackson.core.type.TypeReference;

List<User> users = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});

Il modello ad albero: quando la struttura è sconosciuta

Quando non hai (o non vuoi) una classe corrispondente, analizza il JSON in un albero generico JsonNode e naviga per chiave. Questo è analogo a un parser manuale basato su Map/List, ma con accessor tipizzati come asInt() e asText().

import com.fasterxml.jackson.databind.JsonNode;

JsonNode root = mapper.readTree(json);
String name = root.get("name").asText();
int age     = root.get("age").asInt();
JsonNode first = root.get("languages").get(0); // array access by index

Controllare la mappatura con le annotazioni

I nomi dei campi JSON raramente corrispondono perfettamente alle convenzioni Java. Un insieme ridotto di annotazioni colma il divario senza dover cambiare i nomi dei campi:

AnnotazioneEffetto
@JsonProperty("user_name")Mappa un campo a una chiave JSON diversa
@JsonIgnoreOmette un campo in entrambe le direzioni
@JsonInclude(NON_NULL)Rimuove i campi null dall'output
@JsonCreator / @JsonFormatCostruzione personalizzata / formattazione delle date
record Account(
    @JsonProperty("user_name") String userName,
    @JsonIgnore String passwordHash) {}

Un esempio pratico: serializzare, analizzare e associare manualmente

Jackson non è nel classpath di questo runner, quindi il programma seguente riproduce gli stessi concetti — un writer JSON, un parser ricorsivo discendente in un albero Map/List, e l'associazione in un record — usando solo java.util. È esattamente ciò che ObjectMapper fa internamente: percorre un grafo di oggetti per emettere testo, e tokenizza il testo per ricostruire un albero.

java— editable, runs on the server

Cosa osservare dall'esecuzione:

  • La serializzazione percorre un grafo di oggetti e produce testo. La riga serialized mostra un Map/List annidato reso come {"name":"Ada",...,"languages":["Java","Ada"]} — un array all'interno di un oggetto. ObjectMapper.writeValueAsString esegue la stessa visita ricorsiva sui getter del tuo POJO o sui componenti di un record.
  • L'analisi ricostruisce prima un albero generico. Il tipo a runtime del valore analizzato è LinkedHashMap, non User — esattamente il modello ad albero di Jackson, in cui readTree restituisce un JsonNode navigabile per chiave prima che sia coinvolta qualsiasi classe.
  • I numeri JSON diventano numeri Java, con una scelta di tipo. Il campo age è tornato come Integer (42) perché il testo non conteneva un punto decimale; il parser ha scelto Integer anziché Double. Jackson opera la stessa scelta identica, motivo per cui un campo int si associa correttamente mentre 3.14 risulterebbe come Double.
  • L'accesso ai campi avviene per nome e l'ordine è preservato. L'uso di LinkedHashMap ha mantenuto le chiavi nell'ordine di inserimento, così name viene letto prima di age. Jackson preserva allo stesso modo l'ordine delle chiavi dell'oggetto, per questo il JSON dopo un round-trip appare identico all'originale.
  • Il round-trip è privo di perdite quando il modello corrisponde. L'ultima riga ha ri-serializzato l'albero analizzato producendo lo stesso JSON di partenza, e il record tipizzato User ha associato correttamente name, age e languages — è proprio il punto del data binding: testo in ingresso, oggetto in uscita, testo di ritorno, senza deriva.

Pratica

Pratica
Con Jackson, ricevi una risposta JSON la cui struttura non controlli e devi leggere solo due campi da un documento grande e profondamente annidato. Quale approccio è più adatto?
Con Jackson, ricevi una risposta JSON la cui struttura non controlli e devi leggere solo due campi da un documento grande e profondamente annidato. Quale approccio è più adatto?
Was this page helpful?