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:
| Approccio | Tipo principale | Usalo quando |
|---|---|---|
| Data binding | ObjectMapper.readValue / writeValue | Hai un POJO o un record che rispecchia il JSON — il caso più comune |
| Modello ad albero | JsonNode tramite readTree | La struttura è dinamica o hai bisogno solo di alcuni campi |
| Streaming | JsonParser / JsonGenerator | Documenti 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()); // AdaPer 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 indexControllare 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:
| Annotazione | Effetto |
|---|---|
@JsonProperty("user_name") | Mappa un campo a una chiave JSON diversa |
@JsonIgnore | Omette un campo in entrambe le direzioni |
@JsonInclude(NON_NULL) | Rimuove i campi null dall'output |
@JsonCreator / @JsonFormat | Costruzione 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.
Cosa osservare dall'esecuzione:
- La serializzazione percorre un grafo di oggetti e produce testo. La riga
serializedmostra unMap/Listannidato reso come{"name":"Ada",...,"languages":["Java","Ada"]}— un array all'interno di un oggetto.ObjectMapper.writeValueAsStringesegue 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, nonUser— esattamente il modello ad albero di Jackson, in cuireadTreerestituisce unJsonNodenavigabile per chiave prima che sia coinvolta qualsiasi classe. - I numeri JSON diventano numeri Java, con una scelta di tipo. Il campo
ageè tornato comeInteger(42) perché il testo non conteneva un punto decimale; il parser ha sceltoIntegeranzichéDouble. Jackson opera la stessa scelta identica, motivo per cui un campointsi associa correttamente mentre3.14risulterebbe comeDouble. - L'accesso ai campi avviene per nome e l'ordine è preservato. L'uso di
LinkedHashMapha mantenuto le chiavi nell'ordine di inserimento, cosìnameviene letto prima diage. 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
Userha associato correttamentename,ageelanguages— è proprio il punto del data binding: testo in ingresso, oggetto in uscita, testo di ritorno, senza deriva.