1. Lettura di JSON in Map e List
Di solito, quando lavoriamo con JSON, conosciamo in anticipo la sua struttura e possiamo descriverla come una classe Java. Ma in pratica non è sempre così prevedibile: i campi possono comparire e scomparire, la nidificazione può cambiare. In questi casi è molto più comodo operare con strutture dati universali — Map, List — o con alberi JSON.
Creare un modello Java per ogni variazione è lungo e fragile. Le collezioni universali e gli alberi permettono di estrarre in modo flessibile solo le parti necessarie, senza legarsi a uno schema fisso.
Deserializzazione di un oggetto JSON in Map
Esempio di JSON:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"active": true
}
Deserializzazione in Map:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"id\":123,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"active\":true}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> data = mapper.readValue(json, Map.class);
System.out.println(data);
// Output: {id=123, name=Alice, email=alice@example.com, active=true}
}
}
Ora potete accedere a qualsiasi campo come a un elemento della mappa:
System.out.println(data.get("name")); // Alice
Deserializzazione di un array di oggetti in List
Array JSON:
[
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
Deserializzazione:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Map;
public class UsersReadExample {
public static void main(String[] args) throws Exception {
String json = "[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]";
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> users = mapper.readValue(json, List.class);
for (Map<String, Object> user : users) {
System.out.println(user.get("name"));
}
// Output:
// Alice
// Bob
}
}
Nota importante: quando si legge in strutture universali tutti gli oggetti annidati diventano Map, e gli array — List. Per strutture complesse bisognerà eseguire con attenzione i cast dei tipi e verificarli:
Object items = data.get("items");
if (items instanceof List) {
List<?> itemList = (List<?>) items;
// ...
}
2. JsonNode: albero JSON in Jackson
Lavorare con Map/List è comodo, ma non è sicuro: si può sbagliare tipo o perdere un livello di nidificazione. Jackson offre uno strumento più potente — la classe JsonNode. È un albero universale in cui ogni nodo è un oggetto, un array, un valore oppure null.
Ottenere JsonNode
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class JsonNodeStart {
public static void main(String[] args) throws Exception {
String json = "{\"id\":123,\"name\":\"Alice\",\"tags\":[\"java\",\"json\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
// root è la radice dell’albero JSON
}
}
Accesso ai campi
int id = root.get("id").asInt(); // 123
String name = root.get("name").asText(); // Alice
JsonNode tags = root.get("tags"); // array
System.out.println("Nome: " + name);
Scorrere un array
for (JsonNode tag : tags) {
System.out.println(tag.asText());
}
// Output:
// java
// json
Oggetti annidati
Esempio di JSON complesso:
{
"user": {
"id": 1,
"profile": {
"nickname": "java_guru",
"age": 25
}
}
}
Estrazione di un valore annidato:
JsonNode profile = root.get("user").get("profile");
String nickname = profile.get("nickname").asText();
System.out.println(nickname); // java_guru
Accesso sicuro: get vs path
- get("chiave") — se la chiave non esiste, restituisce null. Qualsiasi chiamata come asText() su null porterà a NullPointerException.
- path("chiave") — se la chiave non esiste, restituisce un nodo «vuoto», e asText() darà "", asInt() — 0.
String phone = root.path("phone").asText(); // "" se il campo non esiste
Verifica del tipo di nodo
if (root.has("tags") && root.get("tags").isArray()) {
for (JsonNode tag : root.get("tags")) {
// ...
}
}
Modifica di JsonNode
JsonNode è immutabile. Per creare/modificare l’albero usate ObjectNode/ArrayNode.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
ObjectMapper mapper = new ObjectMapper();
// Creazione di un nuovo oggetto
ObjectNode obj = mapper.createObjectNode();
obj.put("id", 10);
obj.put("name", "Bob");
// Aggiunta di un array
ArrayNode arr = mapper.createArrayNode();
arr.add("Java").add("JSON");
obj.set("tags", arr);
System.out.println(obj.toPrettyString());
/*
{
"id" : 10,
"name" : "Bob",
"tags" : [ "Java", "JSON" ]
}
*/
3. Lavorare con Gson: JsonElement, JsonObject, JsonArray
Jackson non è l’unico attore. Gson fornisce anch’esso un’API comoda per JSON dinamico: JsonElement, JsonObject, JsonArray.
Parsing in JsonElement
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
String json = "{\"id\":123,\"name\":\"Alice\",\"tags\":[\"java\",\"json\"]}";
JsonElement root = JsonParser.parseString(json);
Accesso ai campi
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
JsonObject obj = root.getAsJsonObject();
int id = obj.get("id").getAsInt();
String name = obj.get("name").getAsString();
JsonArray tags = obj.getAsJsonArray("tags");
for (JsonElement tag : tags) {
System.out.println(tag.getAsString());
}
Nidificazione e sicurezza
if (obj.has("email")) {
String email = obj.get("email").getAsString();
}
Lavorare con gli array
String arrJson = "[{\"id\":1},{\"id\":2}]";
JsonArray arr = JsonParser.parseString(arrJson).getAsJsonArray();
for (JsonElement el : arr) {
JsonObject item = el.getAsJsonObject();
System.out.println(item.get("id").getAsInt());
}
Modifica
In Gson gli oggetti sono mutabili — si possono aggiungere e rimuovere campi:
import com.google.gson.JsonObject;
JsonObject newObj = new JsonObject();
newObj.addProperty("id", 42);
newObj.add("tags", tags);
System.out.println(newObj.toString());
4. Pratica: estrarre dati da un JSON sconosciuto
Compito: Supponiamo di avere una configurazione JSON in cui la struttura può cambiare:
{
"service": "mail",
"enabled": true,
"params": {
"host": "smtp.example.com",
"port": 587
}
}
Estrarre host e port con Jackson:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
JsonNode params = root.path("params");
String host = params.path("host").asText();
int port = params.path("port").asInt();
System.out.println(host + ":" + port); // smtp.example.com:587
Lo stesso con Gson:
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
JsonObject rootObj = JsonParser.parseString(json).getAsJsonObject();
JsonObject params = rootObj.getAsJsonObject("params");
String host = params.get("host").getAsString();
int port = params.get("port").getAsInt();
System.out.println(host + ":" + port);
5. Errori tipici e particolarità
Errore n. 1: Cast di tipi errato. Se vi aspettate una stringa ma nel campo c’è un numero o null, la chiamata a asText() o getAsString() produrrà un risultato inatteso o un’eccezione. Ad esempio, in assenza del campo root.get("foo").asText() provocherà una NullPointerException.
Errore n. 2: Mancata verifica di null. Soprattutto con accessi annidati: root.get("params").get("host"). Se params non esiste — avrete una NPE. In Jackson usate path(), in Gson — verificate la presenza con has e il tipo con isJsonObject()/isJsonArray().
Errore n. 3: Confusione sui tipi di nodo. Se un campo è un array ma lo trattate come un oggetto — otterrete un errore. Controllate i tipi: Jackson — isArray()/isObject(), Gson — isJsonArray()/isJsonObject().
Errore n. 4: Perdita di informazioni sui tipi quando si lavora con Map/List. Durante la deserializzazione in Map<String, Object> le strutture annidate diventano una cascata di Map/List, cosa che complica la navigazione e porta a molti cast e controlli instanceof.
Errore n. 5: Mancata comprensione dell’(im)mutabilità dell’albero. In Jackson JsonNode è immutabile; per modificare occorre passare tramite ObjectNode e ArrayNode. In Gson gli oggetti sono mutabili; ricordate che la modifica di un nodo riguarda tutte le referenze ad esso nell’albero corrente.
GO TO FULL VERSION