In letzter Zeit bin ich immer wieder darüber erstaunt, wie oft ich in meiner täglichen Arbeit mit menschenlesbaren Datenformaten zu tun habe. Zuerst bin ich, im Rahmen von Renovate-Konfigurationen, über JSON5 gestolpert. Dann wurde im Kubernetes-Ökosystem mit KYAML eine verbesserte YAML-Variante vorgestellt. Und schließlich kam im JDK eine Diskussion auf, ob es nicht eine gute Idee sei, dort eine JSON-Implementierung einzubauen.

Dabei ist mir dann wieder aufgefallen, dass ich bei der Verarbeitung in Java meistens mit JSON zu tun habe und dafür, durch Spring Boot, meistens Jackson verwende. Über die Frage, was ich mache, wenn ich mal ein anderes Datenformat als die „JavaScript Object Notation“ verarbeiten möchte, bin ich dann wieder darauf gestoßen, dass ich nicht zwangsweise auf andere Lösungen zurückgreifen muss, sondern Jackson eben mehr als nur JSON kann.

Doch bevor wir zu anderen Datenformaten kommen, möchte ich mit den Basics von Jackson anfangen.

Jackson

Jackson gibt es bereits seit 2008 und ist heute im Java-Universum weitverbreitet. Das liegt wohl auch daran, dass es in Spring-Boot-Projekten standardmäßig zur Verarbeitung von JSON eingesetzt wird, dabei häufig in Verbindung mit Databinding. Das bedeutet, dass sowohl zum Serialisieren nach als auch zum Deserialisieren von JSON eigene Objekte genutzt werden. Jackson schreibt oder setzt dann die dort definierten Felder selbst und wir kommen mit den Details von JSON nicht mehr in Berührung.

Im Kern beruht Jackson jedoch auf einer Streaming-API, auf der das bekannte Databinding basiert und die wir auch direkt nutzen können. Das Schreiben von JSON sieht dann wie in Listing 1 zu sehen aus.

try (JsonGenerator generator = new JsonFactory()
      .createGenerator(System.out)
      .useDefaultPrettyPrinter()) {
  generator.writeStartObject();

  generator.writeFieldName("number");
  generator.writeNumber(42);

  generator.writeFieldName("list");
  generator.writeStartArray();
  generator.writeString("a");
  generator.writeString("b");
  generator.writeString("c");
  generator.writeEndArray();

  generator.writeEndObject();
}
Listing 1: JSON mit Jackson Streaming-API schreiben

Der Vorteil dieser API liegt darin, dass wir nicht erst das komplette JSON erzeugen müssen, sondern der JsonGenerator bei jedem Aufruf direkt schreiben kann und so das Ergebnis eben Schritt für Schritt gerendert wird. Das ist nicht nur der schnellste Weg, sondern spart auch Speicher, weil wir keine zusätzlichen Objekte erzeugen müssen. Dafür ist das API allerdings kleinschrittig und wir müssen uns mit den Details von JSON auskennen. Noch deutlicher können wir das bei der Nutzung des JsonParser, siehe Listing 2, sehen.

var json = """
    {
      "number" : 42,
      "list" : [ "a", "b", "c" ]
    }
    """;

var factory = JsonFactory.builder().build();
try (JsonParser parser = factory.createParser(json)) {
  parser.nextToken(); // skip {

  var fieldName = parser.nextFieldName();
  var fieldValue = parser.nextIntValue(0);
  System.out.println(fieldName + "=" + fieldValue);

  fieldName = parser.nextFieldName();
  parser.nextToken(); // skip [

  var list = new ArrayList<String>();
  list.add(parser.nextTextValue());
  list.add(parser.nextTextValue());
  list.add(parser.nextTextValue());
  System.out.println(fieldName + "=" + list);
}
Listing 2: JSON mit Jackson Streaming-API lesen

Das Beispiel funktioniert zwar, aber bereits eine Änderung der Reihenfolge beider Felder würde zu einem Fehler führen, obwohl es sich logisch um die gleichen Objekte handelt. Angesichts dessen wird die Streaming-API fast ausschließlich von anderen Bibliotheken genutzt, beispielsweise intern in der Databinding-API von Jackson.

JSON

Die Databinding-API erlaubt es uns, mittels der Klasse ObjectMapper, mit einem Methodenaufruf direkt aus einem Java-Objekt JSON zu erzeugen oder aus JSON eine fertig gefüllte Instanz eines Java-Objekts zu erhalten. Listing 3 zeigt uns dabei einen Roundtrip für die Klasse JsonBasics.

public class JsonBasics {
  private String a;
  private int b;

  private JsonBasics() {
  }

  public JsonBasics(String a, int b) {
    this.a = a;
    this.b = b;
  }

  public String getA() {
    return a;
  }

  public int getB() {
    return b;
  }

  @Override
  public String toString() {
    return "JsonBasics[" + a + "," + b + "]";
  }

  public static void main(String[] args)
        throws JsonProcessingException {
    var om = new ObjectMapper();

    var json = om.writeValueAsString(
            new JsonBasics("Michael", 42));
    System.out.println(json);
    // -> {"a":"Michael","b":42}


    System.out.println(om.readValue(json, JsonBasics.class));
    // -> JsonBasics[Michael,42]

  }
}
Listing 3: JSON mit Jackson Databinding

Standardmäßig nutzt der von uns erzeugte ObjectMapper dabei die Getter des Objekts für die Serialisierung. Diese werden sowohl für den Namen des Felds genutzt als auch, um an den eigentlichen Wert zu kommen. Bei der Deserialisierung wird dann ein parameterloser Konstruktor zur Erzeugung der Instanz genutzt. Gibt es Setter, werden diese auch zum Ermitteln der Feldnamen und zum Setzen der Daten genutzt. Fehlen diese, wird beides per Reflektion anhand der Felder des Objekts gemacht.

Möchte man sich nicht auf diese Standards verlassen oder möchte bewusst abweichen, bietet Jackson eine Reihe von Annotationen, um dies zu übersteuern, siehe Listing 4.

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonInclude(NON_NULL)
public class JacksonAnnotations {
  private final String a;
  @JsonProperty("c")
  private final int b;

  @JsonCreator
  public JacksonAnnotations(
        @JsonProperty("a") String a,
        @JsonProperty("c") int b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public String toString() {
    return "JacksonAnnotations[" + a + "," + b + "]";
  }

  public static void main(String[] args)
        throws JsonProcessingException {
    var om = new ObjectMapper();

    var json = om.writeValueAsString(
            new JacksonAnnotations(null, 42));
    System.out.println(json);
    // -> {"c":42}


    System.out.println(om.readValue(json, JacksonAnnotations.class));
    // -> JacksonAnnotations[null,42]

  }
}
Listing 4: Jackson-Mapping mit Annotationen beeinflussen

Alternativ lassen sich einige der Features auch auf dem ObjectMapper setzen und gelten dann für alle mit diesem durchgeführten Operationen, siehe Listing 5.

public class JacksonFeatures {
  private final String a;
  private final int b;
  private final List<String> c;

  public JacksonFeatures(String a, int b, List<String> c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }

  public static void main(String[] args)
        throws JsonProcessingException {
    var om = new ObjectMapper();
    om.setSerializationInclusion(NON_NULL);
    om.setVisibility(FIELD, ANY);
    om.enable(WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);

    var json = om.writeValueAsString(
            new JacksonFeatures(null, 42, List.of("one")));
    System.out.println(json);
    // -> {"b":42,"c":"one"}

  }
}
Listing 5: Jackson-ObjectMapper mit Features konfigurieren

Außerdem können wir beim ObjectMapper auch Module registrieren. Diese werden vor allem dazu eingesetzt, weitere Datentypen, wie die aus der Java DateTime-API, zu unterstützen (siehe Listing 6).

public class JacksonModules {
  private final LocalDate date;

  public JacksonModules(LocalDate date) {
    this.date = date;
  }

  public LocalDate getDate() {
    return date;
  }

  public static void main(String[] args)
        throws JsonProcessingException {
    var om = new ObjectMapper();
    om.registerModule(new JavaTimeModule());
    om.disable(WRITE_DATES_AS_TIMESTAMPS);

    var json = om.writeValueAsString(
            new JacksonModules(LocalDate.of(1986, 9, 23)));
    System.out.println(json);
    // -> {"date":"1986-09-23"}

  }
}
Listing 6: Jackson-ObjectMapper mit Modulen erweitern

Durch diese Möglichkeiten ist Jackson sehr weit konfigurierbar. Sogar so weit, dass wir das in Listing 7 zu sehende valide JSON5 parsen können.

{
  greeting: 'Hallo \

  Welt',
  values: [ 1, 2, ],
  a: .1,
  // this is a comment

  b: 1.,
  c: 1.1,
  d: +3,
  e: +3.,
  /*
  This is another comment
  */
  f: +.3,
  g: NaN,
  h: +INF,
}
Listing 7: JSON5-Beispiel

Das Ziel von JSON5 besteht dabei primär darin, das Schreiben von JSON-basierten Konfigurationen für Menschen einfacher zu gestalten. Dazu wird eine Reihe von ECMAScript-5.1-Features unterstützt. Vor allem die in Listing 7 zu sehende Unterstützung von Kommentaren, Trailing-Kommas und Feldnamen ohne Quoting machen einem dabei das Leben leichter. Um ein solches JSON5-Dokument mit Jackson zu lesen, können wir die in Listing 8 zu sehende Konfiguration nutzen.

public class Json5 {
  public String greeting;
  public List<Integer> values;
  public float a;
  public float b;
  public float c;
  public float d;
  public float e;
  public float f;
  public Number g;
  public Number h;

  @Override
  public String toString() {
    return """
            Json5 {
                greeting: "%s"
                values: %s
                a: %f
                b: %f
                c: %f
                d: %f
                e: %f
                f: %f
                g: %f
                h: %f
            }
            """.formatted(greeting, values, a, b, c, d, e, f, g, h);
  }

  public static void main(String[] args) throws IOException {
    var factory = JsonFactory.builder()
            .enable(ALLOW_UNQUOTED_FIELD_NAMES)
            .enable(ALLOW_TRAILING_COMMA)
            .enable(ALLOW_SINGLE_QUOTES)
            .enable(ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)
            .enable(ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS)
            .enable(ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
            .enable(ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS)
            .enable(ALLOW_NON_NUMERIC_NUMBERS)
            .enable(ALLOW_JAVA_COMMENTS)
            .enable(ALLOW_UNESCAPED_CONTROL_CHARS)
            .build();
    var om = new ObjectMapper(factory);

    var content = om.readValue(new File("test.json5"), Json5.class);
    System.out.println(content);
    /* ->
    Json5 {
        greeting: "Hallo
      Welt"
        values: [1, 2]
        a: 0.100000
        b: 1.000000
        c: 1.100000
        d: 3.000000
        e: 3.000000
        f: 0.300000
        g: NaN
        h: Infinity
    */
  }
}
Listing 8: Mit Jackson JSON5 lesen

Und obwohl wir damit nicht die gesamte JSON5-Spezifikation unterstützen, zeigt dieses Beispiel gut, wie mächtig Jackson sein kann. Und dadurch, dass die, aus meiner Sicht, Hauptfeatures von JSON5 funktionieren, spricht auch nichts gegen einen Einsatz von Jackson zu diesem Zweck.

Doch neben der Flexibilität in Bezug auf das Lesen und Schreiben von JSON ist Jackson auch in einer zweiten Dimension flexibel. Jackson bietet nämlich neben JSON auch Unterstützung für weitere menschenlesbare Datenformate, die wir uns im Folgenden anschauen wollen.

CSV und verwandte Formate

CSV für „Comma-Separated Values“ ist vermutlich das älteste Datenformat, das wir in diesem Artikel betrachten. Und trotz seines Alters habe ich regelmäßig in Projekten die Anforderung, Daten in diesem Format bereitzustellen oder zu konsumieren. Das liegt primär daran, dass es perfekt dazu geeignet ist, Daten, die in einer tabellenartigen Struktur benötigt werden, darzustellen. Außerdem kann dieses Format ohne großen Aufwand in Microsoft Excel sowohl im- als auch exportiert werden.

Klassischerweise beginnt das Format mit einer Header-Zeile, bei der die Namen der enthaltenen Spalten, getrennt durch Kommas, aufgelistet werden. Anschließend kommen die eigentlichen Daten, ein Eintrag pro Zeile und dieser auch pro Spalte durch ein Komma getrennt (siehe Listing 9).

name;number;date;strings
Michael;39;"1986-09-23";"1,2"
;42;2025-09-19;
Listing 9: CSV-Beispiel

Da dieses Format auf den ersten Blick einfach erscheint, ist es verlockend, das Schreiben mittels Konkatenation von Strings und das Lesen durch das Splitten von Strings zu erledigen. Trotzdem lohnt es sich, meiner Meinung nach, auf eine vorhandene und erprobte Bibliothek zurückzugreifen. Denn was passiert beispielsweise, wenn ein Wert selbst ein Komma enthält? Wir müssen diesen Wert escapen. Natürlich lässt sich auch das in eigenem Code erledigen, aber schnell landen wir bei einer eigenen CSV-Implementierung, die anschließend von uns gewartet werden muss.

Neben CSV-spezifischen Bibliotheken, wie Apache Commons CSV, können wir dazu auch Jackson CSV, siehe Listing 10, nutzen.

@JsonPropertyOrder({"name", "number", "date", "strings"})
public class JacksonCsv {
  private String name;
  private int number;
  private LocalDate date;
  private List<String> strings;

  @Override
  public String toString() {
    return "JacksonCsv["
            + name + ","
            + number + ","
            + date + ","
            + strings + "]";
  }

  public static void main(String[] args) throws IOException {
    var mapper = new CsvMapper();
    mapper.setVisibility(FIELD, ANY);
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(WRITE_DATES_AS_TIMESTAMPS);

    var schema = mapper.schemaFor(JacksonCsv.class)
            .withLineSeparator("\n")
            .withColumnSeparator(';')
            .withHeader()
            .withArrayElementSeparator(",");

    var csv = """
            name;number;date;strings
            Michael;39;"1986-09-23";"1,2"
            ;42;2025-09-19;
            """;

    try (var it = mapper
            .readerFor(JacksonCsv.class)
            .with(schema)
            .readValues(csv)) {
        var objects = it.readAll();
        objects.forEach(System.out::println);
        /* ->
        JacksonCsv[Michael,39,1986-09-23,[1, 2]]
        JacksonCsv[,42,2025-09-19,[]]
         */
    }

    try (var writer = mapper.writer(schema).writeValues(System.out)) {
        writer.write(new JacksonCsv());
        /* ->
        name;number;date;strings
        ;0;;
         */
    }
  }
}
Listing 10: Mit Jackson CSV lesen und schreiben

Wir nutzen hierzu zwar mit CsvMapper eine für CSV spezifische Implementierung und nicht wie vorher den ObjectMapper, dafür können wir allerdings andere Bausteine, wie das Modul für das Java DateTime-API, wiederverwenden.

Zu beachten ist dabei allerdings, dass verschachtelte Objekte nicht unterstützt werden. Somit müssen wir in der Regel für CSV spezifische Klassen erzeugen, um Objektstrukturen selbst flach zu machen. Eine Ausnahme bilden Arrays und Listen, die unterstützt werden. Als Alternative zu eigenen Objekten können wir auch ein Array oder eine Liste, siehe Listing 11, an write übergeben, um eine Zeile zu repräsentieren.

public class JacksonCsvGeneric {
  public static void main(String[] args) throws IOException {
    var schema = CsvSchema.builder()
            .addColumn("name", STRING)
            .addColumn("age", NUMBER)
            .setLineSeparator('\n')
            .setColumnSeparator(';')
            .setUseHeader(true)
            .build();

    var mapper = new CsvMapper();

    try (var writer = mapper.writer(schema).writeValues(System.out)) {
        writer.write(List.of("Michael", 39));
        writer.write(new Object[] { "", -7 });
        /* ->
        name;age
        Michael;39
        ;-7
        */
    }
  }
}
Listing 11: Mit Jackson CSV ohne eigene Objekte schreiben

XML

Vor 15 Jahren war die „Extensible Markup Language“ XML das universell eingesetzte Datenformat und ist einem nahezu überall begegnet. Von Konfigurationsdateien bis zur Datenübertragung per SOAP, XHTML oder Ajax. Ja, das X dort steht ursprünglich für XML. Und auch heute haben wir immer noch vereinzelt, beispielsweise in der Maven-POM, mit XML zu tun. XML, siehe Listing 12, hat dabei den Ruf, geschwätzig und ausschweifend zu sein und dadurch an Lesbarkeit zu verlieren.

<example age="39">
  <name>Michael</name>
  <date>1986-09-23</date>
  <strings>
    <string>one</string>
    <string>2</string>
  </strings>
</example>
Listing 12: XML-Beispiel

Dagegen steht durch Features wie Schemas, XSLT oder XPATH auch eine ausgezeichnete Tooling-Unterstützung im Alltag, die es mit JSON-Schema und JSONPath heute auch bis ins JSON-Universum geschafft haben.

Da XML so verbreitet war, hatten wir im JDK mit:

  • der „Java API for XML Processing“ JAXP und
  • der „Java Architecture for XML Binding“ JAXB

gleich zwei Wege zur Verarbeitung im JDK. JAXP gleicht dabei ein wenig der Jackson Streaming-API, wohingegen JAXB die Databinding-Variante abbildet. Und auch wenn wir heute beides noch nutzen können, gibt es mit Jackson XML, siehe Listing 13, auch die Möglichkeit, Jackson zu nutzen.

public class JacksonXml {
  private String name;
  private int age;
  private LocalDate date;
  private List<String> strings;

  public JacksonXml(
        String name, int age, LocalDate date, List<String> strings) {
    this.name = name;
    this.age = age;
    this.date = date;
    this.strings = strings;
  }

  public static void main(String[] args)
        throws JsonProcessingException {
    var xm = new XmlMapper();
    xm.setVisibility(FIELD, ANY);
    xm.registerModule(new JavaTimeModule());
    xm.enable(INDENT_OUTPUT);
    xm.disable(WRITE_DATES_AS_TIMESTAMPS);

    var xml = xm.writeValueAsString(
            new JacksonXmlNo("Michael", 39,
              LocalDate.of(1986, 9, 23), List.of("one", "2")));
    System.out.println(xml);
    /* ->
    <JacksonXml>
      <name>Michael</name>
      <age>39</age>
      <date>1986-09-23</date>
      <strings>
        <strings>one</strings>
        <strings>2</strings>
      </strings>
    </JacksonXml>
    */
  }
}
Listing 13: Mit Jackson XML verarbeiten

Da XML, unabhängig von der Darstellung, an vielen Stellen sehr ähnlich zu JSON ist, funktioniert das Databinding identisch und damit ausgezeichnet. Ergänzend zu den aus JSON bekannten Annotationen für die Konfiguration gibt es zusätzliche eigene, XML spezifische Annotationen, siehe Listing 14.

@JacksonXmlRootElement(localName = "example")
public class JacksonXml {

  private String name;
  @JacksonXmlProperty(isAttribute = true)
  private int age;
  private LocalDate date;
  @JacksonXmlElementWrapper(localName = "strings")
  @JacksonXmlProperty(localName = "string")
  private List<String> strings;

  public JacksonXml(
      String name, int age, LocalDate date, List<String> strings) {
    this.name = name;
    this.age = age;
    this.date = date;
    this.strings = strings;
  }

  public static void main(String[] args)
      throws JsonProcessingException {
    var xm = new XmlMapper();
    xm.setVisibility(FIELD, ANY);
    xm.registerModule(new JavaTimeModule());
    xm.enable(INDENT_OUTPUT);
    xm.disable(WRITE_DATES_AS_TIMESTAMPS);

    var xml = xm.writeValueAsString(
        new JacksonXml("Michael", 39, LocalDate.of(1986, 9, 23), List.of("one", "2")));
    System.out.println(xml);
    /* ->
    <example age="39">
      <name>Michael</name>
      <date>1986-09-23</date>
      <strings>
        <string>one</string>
        <string>2</string>
      </strings>
    </example>
    */
  }
}
Listing 14: Jackson-XML-Annotationen zur Konfiguration

Alternativ ist es auch noch möglich, die von JAXB standardisierten Annotationen zu verwenden.

YAML

Für Konfiguration hat sich heute an vielen Stellen YAML gegenüber XML durchgesetzt. Vor allem in der DevOps-Welt spricht der ein oder andere Mensch schon davon, YAML-Engineer zu sein. Die kompakte Schreibweise, siehe Listing 15, macht Konfigurationen dabei deutlich kürzer als bei der Nutzung von XML oder JSON und damit auch vermeintlich lesbarer.

a: someString

b: no

c:

- a

- 1

- yes
Listing 15: YAML-Beispiel

Das kann dann allerdings auch für Überraschungen sorgen, wie das YAML-Quiz demonstriert. So werden im obigen Beispiel beispielsweise no und yes nicht wie eventuell erwartet als Zeichen, sondern als Wahrheitswerte interpretiert.

Da jedes YAML-Dokument zu JSON konvertiert werden kann, wird YAML häufig wirklich nur als Konfigurationsformat genutzt und nicht in der eigentlichen Datenübertragung verwendet.

Als Bibliothek für Java wird zum Parsen von YAML häufig SnakeYAML verwendet. Aber auch hier können wir als Alternative Jackson YAML nutzen, siehe Listing 16.

@JsonAutoDetect(fieldVisibility = ANY)
public class JacksonYaml {
  String a;
  boolean b;
  List<Object> c;

  public static void main(String[] args) throws Exception {
    var yaml = """
            a: someString
            b: no
            c:
            - a
            - 1
            - yes
            """;
    var ym = new YAMLMapper();
    var object = ym.readValue(yaml, JacksonYaml.class);
    System.out.println(object.a);
    // -> someString

    System.out.println(object.b);
    // -> false

    System.out.println(object.c);
    // -> [a, 1, true]

  }
}
Listing 16: Mit Jackson YAML lesen

Da Jackson unter der Haube selbst SnakeYAML verwendet, besteht der Vorteil hier vorwiegend darin, das bekannte Programmiermodell von Jackson verwenden zu können.

TOML

Als Alternative zu YAML findet man mittlerweile auch häufig TOML. TOML versucht dabei, die Einfachheit von JSON mit der Lesbarkeit von YAML zu kombinieren, siehe Listing 17.

booleans = true
lists = [ 1, "Hallo", false ]

[nested]
# this is a comment
a = "Hallo"
someNumber = 42
dateIsNative = 1986-09-23
Listing 17: TOML-Beispiel

So werden, im Vergleich zu JSON, Kommentare unterstützt und es gibt weitere nativ unterstützte Datentypen, wie Datumswerte. Dabei wird ein flaches, zeilenbasiertes Format, das an INI-Dateien erinnert, ähnlich wie YAML genutzt.

Und natürlich wird auch dieses Format von Jackson TOML, siehe Listing 18, unterstützt.

@JsonAutoDetect(fieldVisibility = ANY)
public class JacksonToml {
  boolean a;
  List<Object> b;
  Nested c;

  @JsonAutoDetect(fieldVisibility = ANY)
  static class Nested {
    String a;
    int b;
    LocalDate c;
  }

  public static void main(String[] args) throws Exception {
    var toml = """
            a = true
            b = [ 1, "Hallo", false ]
            [c]
            a = "Hallo"
            b = 42
            c = 1986-09-23
            """;
    var tm = new TomlMapper();
    tm.registerModule(new JavaTimeModule());

    var object = tm.readValue(toml, JacksonToml.class);

    System.out.println(object.a);
    // -> true

    System.out.println(object.b);
    // -> [1, Hallo, false]

    System.out.println(object.c.a);
    // -> Hallo

    System.out.println(object.c.b);
    // -> 42

    System.out.println(object.c.c);
    // -> 1986-09-23

  }
}
Listing 18: Mit Jackson TOML lesen

Wie auch bei den anderen Datenformaten vorher erstellen wir dazu einen Mapper, in diesem Fall TomlMapper, und nutzen diesen anschließend für Serialisierung und Deserialisierung.

Conclusion

Jackson ist als Bibliothek zur Verarbeitung von JSON im Java-Universum weithin bekannt. Nach einer kurzen Einführung in die Basics haben wir in diesem Artikel auch gesehen, dass Jackson eben nicht nur für JSON, sondern auch für andere Formate genutzt werden kann.

Neben den hier gezeigten menschenlesbaren Datenformaten JSON, CSV, XML, YAML und TOML gibt es auch noch Support für weitere. So werden auch Properties und diverse binäre Formate, wie Avro oder Protobuf, unterstützt.

Setzen wir im Projekt bereits Jackson für die Verarbeitung von JSON ein, lohnt sich somit auch ein Blick auf die anderen Module. Im konkreten Fall kann zwar eine der spezifischen Bibliotheken schneller oder effizienter sein, aber der durch Jackson entstehende gleichförmige Code hat auch einen Wert, den es abzuwägen gilt.