Java JSON mit Jackson
JSON in Java mit Jackson parsen, erzeugen und binden — ObjectMapper und Data Binding.
Jackson ist die de-facto-Standardbibliothek für JSON in Java. Das JDK liefert keine eigene JSON-API, weshalb nahezu jede Spring-, Quarkus- oder Micronaut-Anwendung Jackson verwendet, um Java-Objekte in JSON-Text umzuwandeln und umgekehrt. Der Einstiegspunkt ist eine einzige zentrale Klasse — ObjectMapper — die die zwei Aufgaben übernimmt, die man immer benötigt: Serialisierung (Java-Objekt → JSON) und Deserialisierung (JSON → Java-Objekt).
Wenn Sie neu bei JSON in Java sind, beginnen Sie mit der JSON-Einführung. Für eine schlankere alternative Bibliothek lesen Sie das Gson-Kapitel. Diese Seite behandelt das Hinzufügen von Jackson, die drei Ebenen seiner API, Data Binding, das Baummodell und die Annotationen, die das Mapping steuern.
Jackson zum Projekt hinzufügen
Jackson ist nicht Bestandteil des JDK, daher deklarieren Sie es als Abhängigkeit. Das Artefakt jackson-databind bindet die beiden anderen Kernmodule (jackson-core und jackson-annotations) transitiv ein.
<!-- 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'Die drei Wege, mit JSON zu arbeiten
Jackson stellt dieselben Daten auf drei verschiedene Arten bereit. Wählen Sie die Ebene, die zur Aufgabe passt:
| Ansatz | Kerntyp | Verwenden, wenn |
|---|---|---|
| Data Binding | ObjectMapper.readValue / writeValue | Sie haben ein POJO oder Record, das den JSON widerspiegelt — der häufigste Fall |
| Baummodell | JsonNode über readTree | Die Struktur ist dynamisch oder Sie benötigen nur wenige Felder |
| Streaming | JsonParser / JsonGenerator | Sehr große Dokumente, die nicht vollständig im Speicher gehalten werden können |
Data Binding verwenden Sie in 90 % der Fälle; die anderen beiden sind Ausweichmöglichkeiten.
Data Binding: Objekte rein, JSON raus
ObjectMapper.writeValueAsString serialisiert jedes Java-Objekt, indem es dessen Getter (oder Record-Komponenten) liest; readValue macht das Gegenteil und ordnet JSON-Feldnamen Ihren Feldern zu. Ein record ist das sauberste Ziel — Jackson 2.12+ bindet automatisch an seinen kanonischen Konstruktor, sodass Sie keine Getter oder einen No-Arg-Konstruktor schreiben müssen.
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()); // AdaFür Collections und Generics übergeben Sie Jackson eine TypeReference, damit der Elementtyp die Typlöschung überlebt:
import com.fasterxml.jackson.core.type.TypeReference;
List<User> users = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});Das Baummodell: wenn die Struktur unbekannt ist
Wenn Sie keine passende Klasse haben (oder haben möchten), parsen Sie in einen generischen JsonNode-Baum und navigieren Sie ihn nach Schlüssel. Dies spiegelt wider, was ein manuell erstellter Map/List-Parser tut, aber mit typbewussten Accessoren wie asInt() und 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 indexDas Mapping mit Annotationen steuern
JSON-Feldnamen entsprechen selten perfekt den Java-Konventionen. Eine Handvoll Annotationen schließt die Lücke, ohne Ihre Feldnamen zu ändern:
| Annotation | Wirkung |
|---|---|
@JsonProperty("user_name") | Ein Feld auf einen anderen JSON-Schlüssel abbilden |
@JsonIgnore | Ein Feld in beide Richtungen auslassen |
@JsonInclude(NON_NULL) | null-Felder aus der Ausgabe weglassen |
@JsonCreator / @JsonFormat | Benutzerdefinierte Konstruktion / Datumsformatierung |
record Account(
@JsonProperty("user_name") String userName,
@JsonIgnore String passwordHash) {}Ein vollständiges Beispiel: serialisieren, parsen und manuell binden
Jackson ist im Classpath dieses Runners nicht vorhanden, daher baut das folgende Programm dieselben Konzepte nach — einen JSON-Writer, einen rekursiv-absteigenden Parser in einen Map/List-Baum und das Binden in einen record — ausschließlich mit java.util. Das ist genau das, was ObjectMapper im Hintergrund für Sie tut: einen Objektgraphen durchlaufen, um Text zu erzeugen, und Text tokenisieren, um einen Baum neu aufzubauen.
Was man aus der Ausführung mitnimmt:
- Serialisierung durchläuft einen Objektgraphen und erzeugt Text. Die
serialized-Zeile zeigt eine verschachtelteMap/List, die zu{"name":"Ada",...,"languages":["Java","Ada"]}gerendert wird — ein Array innerhalb eines Objekts.ObjectMapper.writeValueAsStringführt denselben rekursiven Durchlauf über die Getter Ihres POJOs oder die Komponenten eines Records durch. - Das Parsen baut zuerst einen generischen Baum auf. Der Laufzeittyp des geparsten Werts ist
LinkedHashMap, nichtUser— genau das Baummodell von Jackson, bei demreadTreeeinenJsonNodezurückgibt, den Sie nach Schlüssel navigieren, bevor eine Klasse beteiligt ist. - JSON-Zahlen werden zu Java-Zahlen, mit einer Typentscheidung. Das Feld
agekam alsInteger(42) zurück, weil der Text keinen Dezimalpunkt hatte; der Parser wählteIntegerstattDouble. Jackson trifft dieselbe Entscheidung, weshalb einint-Feld sauber gebunden wird, während3.14alsDoubleankäme. - Der Feldzugriff erfolgt nach Name, und die Reihenfolge wird beibehalten. Die Verwendung von
LinkedHashMapbewahrte die Schlüssel in Einfügereihenfolge, sodassnamevoragegelesen wird. Jackson bewahrt die Schlüsselreihenfolge von Objekten auf dieselbe Weise, weshalb ein round-tripped JSON wie das Original aussieht. - Round-Tripping ist verlustfrei, wenn das Modell übereinstimmt. Die letzte Zeile re-serialisierte den geparsten Baum zum selben JSON, aus dem er stammte, und der typisierte
User-Record bandname,ageundlanguageskorrekt — der eigentliche Zweck von Data Binding: Text rein, Objekt raus, Text zurück, kein Datenverlust.