1. JSON in Map und List einlesen
Normalerweise kennen wir beim Arbeiten mit JSON seine Struktur im Voraus und können sie als Java-Klasse beschreiben. In der Praxis ist jedoch nicht alles so vorhersehbar: Felder können erscheinen und verschwinden, die Verschachtelung kann sich ändern. In solchen Fällen ist es viel bequemer, mit universellen Datenstrukturen – Map, List – oder JSON-Bäumen zu arbeiten.
Für jede Variante ein Java-Modell zu erstellen, ist zeitaufwändig und fragil. Universelle Collections und Bäume erlauben es, flexibel nur die benötigten Teile zu extrahieren, ohne an ein fixes Schema gebunden zu sein.
Deserialisierung eines JSON-Objekts in Map
JSON-Beispiel:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"active": true
}
Deserialisierung 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);
// Ausgabe: {id=123, name=Alice, email=alice@example.com, active=true}
}
}
Jetzt können Sie auf jedes Feld wie auf ein Element einer Map zugreifen:
System.out.println(data.get("name")); // Alice
Deserialisierung eines Objektarrays in List
JSON-Array:
[
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
Deserialisierung:
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"));
}
// Ausgabe:
// Alice
// Bob
}
}
Wichtiger Hinweis: Beim Einlesen in universelle Strukturen werden alle verschachtelten Objekte zu Map, und Arrays — zu List. Für komplexe Strukturen müssen Sie die Typen sorgfältig casten und prüfen:
Object items = data.get("items");
if (items instanceof List) {
List<?> itemList = (List<?>) items;
// ...
}
2. JsonNode: JSON-Baum in Jackson
Mit Map/List zu arbeiten ist bequem, aber nicht ganz sicher: Man kann sich im Typ irren oder eine Verschachtelung übersehen. Jackson bietet ein leistungsfähigeres Werkzeug — die Klasse JsonNode. Das ist ein universeller Baum, in dem jeder Knoten — Objekt, Array, Wert oder null ist.
JsonNode erhalten
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 – ist die Wurzel des JSON-Baums
}
}
Zugriff auf Felder
int id = root.get("id").asInt(); // 123
String name = root.get("name").asText(); // Alice
JsonNode tags = root.get("tags"); // Array
System.out.println("Name: " + name);
Durchlaufen eines Arrays
for (JsonNode tag : tags) {
System.out.println(tag.asText());
}
// Ausgabe:
// java
// json
Verschachtelte Objekte
Beispiel für ein komplexes JSON:
{
"user": {
"id": 1,
"profile": {
"nickname": "java_guru",
"age": 25
}
}
}
Verschachtelten Wert extrahieren:
JsonNode profile = root.get("user").get("profile");
String nickname = profile.get("nickname").asText();
System.out.println(nickname); // java_guru
Sicherer Zugriff: get vs path
- get("key") — wenn der Schlüssel fehlt, gibt null zurück. Jeder Aufruf wie asText() auf null führt zu NullPointerException.
- path("key") — wenn der Schlüssel fehlt, gibt einen „leeren“ Knoten zurück, und asText() liefert "", asInt() — 0.
String phone = root.path("phone").asText(); // "" wenn das Feld fehlt
Typ des Knotens prüfen
if (root.has("tags") && root.get("tags").isArray()) {
for (JsonNode tag : root.get("tags")) {
// ...
}
}
Modifikation JsonNode
JsonNode ist unveränderlich. Zum Erstellen/Ändern eines Baums verwenden Sie 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();
// Neues Objekt erstellen
ObjectNode obj = mapper.createObjectNode();
obj.put("id", 10);
obj.put("name", "Bob");
// Array hinzufügen
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. Arbeiten mit Gson: JsonElement, JsonObject, JsonArray
Jackson ist nicht der einzige Player. Gson bietet ebenfalls eine praktische API für dynamisches JSON: 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);
Zugriff auf Felder
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());
}
Verschachtelung und Sicherheit
if (obj.has("email")) {
String email = obj.get("email").getAsString();
}
Arbeiten mit Arrays
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());
}
Modifikation
In Gson sind Objekte veränderlich — man kann Felder hinzufügen und entfernen:
import com.google.gson.JsonObject;
JsonObject newObj = new JsonObject();
newObj.addProperty("id", 42);
newObj.add("tags", tags);
System.out.println(newObj.toString());
4. Praxis: Daten aus einem unbekannten JSON extrahieren
Aufgabe: Angenommen, wir haben eine JSON-Konfiguration, deren Struktur sich ändern kann:
{
"service": "mail",
"enabled": true,
"params": {
"host": "smtp.example.com",
"port": 587
}
}
Extrahieren host und port mit 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
Dasselbe mit 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. Typische Fehler und Besonderheiten
Fehler Nr. 1: Falsches Casten. Wenn Sie eine Zeichenkette erwarten, das Feld jedoch eine Zahl oder null enthält, liefern asText() oder getAsString() ein unerwartetes Ergebnis oder eine Ausnahme. Wird das Feld beispielsweise nicht gefunden, führt root.get("foo").asText() zu NullPointerException.
Fehler Nr. 2: Keine Prüfung auf null. Besonders bei verschachtelten Zugriffen: root.get("params").get("host"). Fehlt params — gibt es eine NPE. In Jackson verwenden Sie path(), in Gson — prüfen Sie die Existenz mit has und den Typ mit isJsonObject()/isJsonArray().
Fehler Nr. 3: Verwechslung der Knotentypen. Wenn ein Feld ein Array ist, Sie es aber wie ein Objekt behandeln — erhalten Sie einen Fehler. Prüfen Sie die Typen: Jackson — isArray()/isObject(), Gson — isJsonArray()/isJsonObject().
Fehler Nr. 4: Informationsverlust über Typen bei Arbeit mit Map/List. Bei der Deserialisierung in Map<String, Object> werden verschachtelte Strukturen zu einer Kaskade aus Map/List, was die Navigation erschwert und zu zahlreichen Casts und Prüfungen mit instanceof führt.
Fehler Nr. 5: Missverständnisse zur (Un-)Veränderlichkeit des Baums. In Jackson ist JsonNode unveränderlich, und Änderungen erfolgen über ObjectNode und ArrayNode. In Gson sind Objekte veränderlich; beachten Sie, dass das Ändern eines Knotens alle Verweise darauf im aktuellen Baum betrifft.
GO TO FULL VERSION