1. Einführung in Jackson
Der Datenaustausch zwischen einer Java-Anwendung und der Außenwelt lässt sich sehr oft auf die Frage bringen: „Wie verwandle ich ein Java-Objekt in JSON – und zurück?“ Man kann natürlich seinen eigenen Parser mit String.split und regulären Ausdrücken schreiben (und sich dabei sogar an der Qual erfreuen), aber in realen Projekten macht das niemand.
In Java gibt es mehrere populäre Bibliotheken für die Arbeit mit JSON. Die wichtigste davon ist Jackson. Sie ist so verbreitet, dass sie Teil von Spring Boot ist und in vielen anderen Frameworks und Bibliotheken verwendet wird.
Was ist Jackson?
Jackson ist eine leistungsfähige und flexible Bibliothek für Serialisierung (Umwandlung eines Java-Objekts in JSON) und Deserialisierung (die umgekehrte Operation). Sie besteht aus mehreren Modulen, aber für 90 % der Aufgaben benötigen Sie zwei:
- jackson-core — Kern, Low-Level-Parser.
- jackson-databind — High-Level-Modul, das Java-Objekte in JSON und zurück verwandeln kann.
Alles, was Sie für den Anfang wissen müssen: Wenn Sie die Klasse ObjectMapper sehen — das ist Jackson.
Jackson gilt de facto als Standard für die Arbeit mit JSON in Java, weil es Einfachheit und Leistungsfähigkeit vereint. Um Daten zu serialisieren oder zu deserialisieren, genügen buchstäblich ein paar Zeilen Code — damit ist die Bibliothek auch für Einsteiger bequem. Gleichzeitig ist sie nicht auf Basisfunktionen beschränkt: Dank Annotationen und zahlreicher Einstellungen lässt sich genau steuern, wie Daten in Objekte und zurück verwandelt werden, seien es Collections, verschachtelte Objekte oder Datumswerte in unterschiedlichen Formaten.
Wichtig ist auch, dass Jackson sehr schnell und sparsam arbeitet — kritisch für reale Projekte mit großen Datenmengen. Die Bibliotheksentwickler unterstützen neue Java-Versionen und aktuelle Änderungen im JSON-Standard — Jackson bleibt eine zuverlässige Wahl sowohl für einfache Anwendungen als auch für große Unternehmenssysteme.
2. JSON lesen (Deserialisierung)
Lassen Sie uns eine JSON-Zeichenkette einlesen und in ein Java-Objekt umwandeln. Dafür benötigen wir:
- Eine Datenklasse (zum Beispiel Person)
- Die Klasse ObjectMapper aus Jackson
Jackson einbinden
Wenn Sie Maven verwenden, fügen Sie in pom.xml hinzu:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
Wenn Sie Gradle verwenden — analog:
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
Beispielklasse
public class Person {
public String name;
public int age;
}
Hinweis: Der Einfachheit halber sind die Felder public. Später besprechen wir den Umgang mit privaten Feldern und Getter/Setter.
Beispiel-JSON
{
"name": "Alice",
"age": 30
}
Deserialisierung: JSON in ein Objekt umwandeln
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"name\": \"Alice\", \"age\": 30}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json, Person.class);
System.out.println(person.name); // Alice
System.out.println(person.age); // 30
}
}
Hier parst Jackson die JSON-Zeichenkette, findet Felder, deren Namen mit den Feldern oder Gettern der Klasse übereinstimmen, und befüllt das Objekt mit den entsprechenden Werten mithilfe von readValue.
Deserialisierung einer Objektliste
Angenommen, wir haben ein Array:
[
{ "name": "Bob", "age": 22 },
{ "name": "Eve", "age": 27 }
]
Wir deserialisieren in eine Liste:
import com.fasterxml.jackson.core.type.TypeReference;
// ...
String json = "[{\"name\": \"Bob\", \"age\": 22}, {\"name\": \"Eve\", \"age\": 27}]";
ObjectMapper mapper = new ObjectMapper();
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
for (Person p : people) {
System.out.println(p.name + " (" + p.age + ")");
}
Vielleicht fragen Sie sich: Warum nicht einfach mapper.readValue(json, List.class) schreiben? Denken Sie daran: Generics werden in Java während der Kompilierung gelöscht (Type Erasure). Deshalb benötigen wir TypeReference, damit Jackson versteht, dass sich in der Liste Objekte vom Typ Person befinden.
3. JSON schreiben (Serialisierung)
Nun führen wir die umgekehrte Operation aus: Wir verwandeln ein Java-Objekt in eine JSON-Zeichenkette.
Beispiel
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String json = mapper.writeValueAsString(person);
System.out.println(json);
// {"name":"Charlie","age":40}
Serialisierung einer Objektliste
ObjectMapper mapper = new ObjectMapper();
List<Person> people = new ArrayList<>();
people.add(new Person("Anna", 25));
people.add(new Person("Dmitry", 31));
String json = mapper.writeValueAsString(people);
System.out.println(json);
// [{"name":"Anna","age":25},{"name":"Dmitry","age":31}]
In eine Datei schreiben
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
mapper.writeValue(new File("person.json"), person);
// Die Datei person.json enthält jetzt ein JSON-Objekt
Schön formatiertes (Pretty) JSON
Standardmäßig schreibt Jackson alles in eine Zeile. Es gibt jedoch auch eine „menschenfreundlichere“ Variante — pretty printing. Das bedeutet, dass die JSON-Zeichenkette in lesbarer Form ausgegeben wird: mit Einrückungen, Zeilenumbrüchen und sauberer Formatierung.
Im Unterschied zu „normalem“ JSON, das der Kompaktheit halber meist in einer Zeile geschrieben wird, ist die „schöne“ Variante für Menschen gedacht — um die Datenstruktur in Logs, Dateien oder auf dem Bildschirm leichter betrachten zu können.
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String prettyJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(person);
System.out.println(prettyJson);
/*
{
"name" : "Charlie",
"age" : 40
}
*/
4. Jackson-Annotationen
Jackson unterstützt eine Vielzahl von Annotationen, mit denen sich der Serialisierungs- und Deserialisierungsprozess steuern lässt. Hier sind die nützlichsten:
@JsonProperty
Ermöglicht es, den Feldnamen im JSON anzugeben, falls er vom Feldnamen in der Klasse abweicht.
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
@JsonProperty("full_name")
public String name;
public int age;
}
{"full_name": "Olga", "age": 28}
Jackson versteht, dass das Feld full_name aus dem JSON in das Feld name des Objekts geschrieben werden soll.
@JsonIgnore
Wenn Sie ein Feld nicht serialisieren oder deserialisieren möchten:
import com.fasterxml.jackson.annotation.JsonIgnore;
public class Person {
public String name;
@JsonIgnore
public int age;
}
Im JSON wird es kein Feld age geben, selbst wenn es im Objekt vorhanden ist.
@JsonInclude
Steuert, welche Felder in das JSON gelangen. Zum Beispiel nur nichtleere Felder serialisieren:
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
public String name;
public Integer age;
}
Wenn age == null ist, gibt es im JSON keinen Schlüssel "age".
@JsonFormat
Ermöglicht es, ein Format für die Serialisierung von Datum und Uhrzeit vorzugeben.
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Event {
public String title;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
public Date date;
}
Event event = new Event();
event.title = "Hackathon";
event.date = new Date();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(event);
// {"title":"Hackathon","date":"2024-06-07 15:23:00"}
Beispiel: alles zusammen
import com.fasterxml.jackson.annotation.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
@JsonProperty("full_name")
private String name;
private int age;
@JsonIgnore
private String password;
// Getter und Setter sind für private Felder obligatorisch!
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
5. Praxis: Serialisierung und Deserialisierung unter Berücksichtigung der Annotationen
Erweitern wir Ihre Lernanwendung — jetzt gibt es eine Klasse User mit privaten Feldern, einem Registrierungsdatum und einem Passwort, das nicht im JSON erscheinen soll.
import com.fasterxml.jackson.annotation.*;
import java.util.Date;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
@JsonProperty("login")
private String username;
private int age;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date registered;
@JsonIgnore
private String password;
// Getter und Setter sind obligatorisch!
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Date getRegistered() { return registered; }
public void setRegistered(Date registered) { this.registered = registered; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Serialisierung
import com.fasterxml.jackson.databind.ObjectMapper;
import java.text.SimpleDateFormat;
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.setUsername("superuser");
user.setAge(42);
user.setRegistered(new SimpleDateFormat("yyyy-MM-dd").parse("2024-06-07"));
user.setPassword("qwerty123"); // Wird nicht im JSON erscheinen!
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
System.out.println(json);
/*
{
"login" : "superuser",
"age" : 42,
"registered" : "2024-06-07"
}
*/
Deserialisierung
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = "{ \"login\": \"superuser\", \"age\": 42, \"registered\": \"2024-06-07\" }";
User user = mapper.readValue(json, User.class);
System.out.println(user.getUsername()); // superuser
System.out.println(user.getAge()); // 42
System.out.println(user.getRegistered());// Fri Jun 07 00:00:00 ...
System.out.println(user.getPassword()); // null (und das ist gut so!)
6. Typische Fehler bei der Arbeit mit Jackson
Fehler Nr. 1: Kein parameterloser Konstruktor.
Jackson kann kein Objekt einer Klasse erzeugen, wenn sie keinen parameterlosen Konstruktor hat. Häufig tritt das in Klassen auf, in denen nur ein Konstruktor mit Argumenten deklariert ist.
Fehler Nr. 2: Private Felder ohne Getter/Setter.
Wenn Sie alle Felder privat gemacht haben, aber Getter und Setter vergessen, kann Jackson sie bei der Deserialisierung (standardmäßig) nicht befüllen.
Fehler Nr. 3: Abweichende Feldnamen.
Wenn der Feldname im JSON vom Feld-/Getter-Namen in Java abweicht, findet Jackson keine Entsprechung. Verwenden Sie @JsonProperty.
Fehler Nr. 4: Falsches Datumsformat.
Wenn das Datumsformat im JSON nicht mit dem in Java erwarteten Format übereinstimmt, wirft Jackson einen Parsing-Fehler. Verwenden Sie @JsonFormat zur Konfiguration.
Fehler Nr. 5: Versuch, Felder mit der Annotation @JsonIgnore zu serialisieren.
Solche Felder gelangen nicht ins JSON — das ist kein Bug, sondern ein Feature.
Fehler Nr. 6: Serialisierung/Deserialisierung von Collections ohne Typangabe.
Ohne TypeReference versteht Jackson nicht, welchen Typ die Objekte in der Collection haben.
Fehler Nr. 7: Exception beim Lesen/Schreiben einer Datei.
Vergessen Sie nicht, IOException beim Arbeiten mit Dateien zu behandeln.
GO TO FULL VERSION