CodeGym /Kurse /JAVA 25 SELF /Arbeiten mit dynamischen Strukturen: Map, List, JsonNode

Arbeiten mit dynamischen Strukturen: Map, List, JsonNode

JAVA 25 SELF
Level 46, Lektion 3
Verfügbar

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.

1
Aufgabe
JAVA 25 SELF, Level 46, Lektion 3
Gesperrt
Daten aus dem Spielprofil extrahieren 🎮
Daten aus dem Spielprofil extrahieren 🎮
1
Aufgabe
JAVA 25 SELF, Level 46, Lektion 3
Gesperrt
Verarbeitung der Städteliste für den Reiseführer 🗺️
Verarbeitung der Städteliste für den Reiseführer 🗺️
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION