CodeGym /Kurse /JAVA 25 SELF /Jackson — Lesen und Schreiben von JSON, Annotationen

Jackson — Lesen und Schreiben von JSON, Annotationen

JAVA 25 SELF
Level 46 , Lektion 1
Verfügbar

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.

1
Aufgabe
JAVA 25 SELF, Level 46, Lektion 1
Gesperrt
Benutzerdatenschutz 🕵️‍♀️
Benutzerdatenschutz 🕵️‍♀️
1
Aufgabe
JAVA 25 SELF, Level 46, Lektion 1
Gesperrt
Verwaltung von Veranstaltungen im Universitätskalender 🗓️
Verwaltung von Veranstaltungen im Universitätskalender 🗓️
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION