Java Videotraining Werbung

Videotraining Spring 3 Boot Werbung

1. XML, JSON und weitere Datenformate mit Java verarbeiten

Zwei wichtige Datenformate für den Austausch von Dokumenten sind XML und JSON. XML ist historisch gesehen der ältere Datentyp, JSON finden wir heutzutage oft in der Kommunikation zwischen einem Server und einer JavaScript-Anwendung. JSON-Dokumente werden auch gerne für Konfigurationsdateien verwendet.

Während die Java SE unterschiedliche Klassen zum Lesen und Schreiben von XML-Dokumenten bietet, ist die Unterstützung von JSON nur in der Java Enterprise Edition oder durch ergänzende Open-Source-Bibliotheken gegeben. Viele der Aufgaben in diesem Kapitel greifen daher zu externen Bibliotheken.

Eine wichtige Kategorie der Dokumentenformate bilden Beschreibungssprachen. Sie definieren die Struktur der Daten. Zu den wichtigsten Formaten zählen HTML, XML, JSON und PDF.

Java bringt bis auf die Unterstützung der Property-Dateien und der Möglichkeit, ZIP-Archive zu verarbeiten, keine Unterstützung für andere Datenformate mit. Das gilt insbesondere auch für CSV-Dateien, PDFs oder Office-Dokumente. Glücklicherweise füllen Dutzende Open-Source-Bibliotheken diese Lücke, sodass man diese Funktionalität nicht selbst programmieren muss.

Voraussetzungen

  • Maven-Dependencies hinzunehmen können

  • StAX kennen

  • XML-Dokumente schreiben können

  • JAXB-Beans aus XML-Schema-Dateien erzeugen können

  • Objekt-XML-Mapping mit Jakarta XML Binding einsetzen können

  • Grundverständnis von Jakarta JSON Bibliotheken

  • ZIP-Archive auslesen können

Verwendete Datentypen in diesem Kapitel:

1.1. XML-Verarbeitung mit Java

Es gibt unterschiedliche Java-APIs zum Umgang mit XML-Dokumenten. Eine Möglichkeit ist das Halten von kompletten XML-Objekten im Speicher, die andere Lösung erinnert an Datenströme. StAX ist eine Pull-API, mit der die Elemente aktiv aus dem Datenstrom gezogen werden und auch geschrieben werden können. Das Verarbeitungsmodell eignet sich optimal für große Dokumente, die nicht komplett im Speicher stehen müssen.

JAXB bietet eine einfache Möglichkeit, Java-Objekte in XML zu konvertieren und XML später wieder in Java-Objekte. Mithilfe von Annotationen oder externen Konfigurationsdateien lässt sich die Abbildung präzise steuern.

1.1.1. XML-Datei mit Rezept schreiben ⭐

Captain CiaoCiao hat so viele Rezepte, dass er eine Datenbank benötigt. Es liegen ihm verschiedene Angebote für Datenbankmanagementsysteme vor, und er möchte schauen, ob sie alle seine Rezepte importieren können.

RecipeML

Seine eigenen Rezepte liegen im RecipeML-Format vor, einem XML-Format, das lose spezifiziert ist: http://www.formatdata.com/recipeml/. Unter https://dsquirrel.tripod.com/recipeml/indexrecipes2.html gibt es eine große Datenbank. Ein Beispiel von »Key Gourmet«:

<?xml version="1.0" encoding="UTF-8"?>
<recipeml version="0.5">
  <recipe>
    <head>
      <title>11 Minute Strawberry Jam</title>
      <categories>
        <cat>Canning</cat>
        <cat>Preserves</cat>
        <cat>Jams &amp; jell</cat>
      </categories>
      <yield>8</yield>
    </head>
    <ingredients>
      <ing>
        <amt>
          <qty>3</qty>
          <unit>cups</unit>
        </amt>
        <item>Strawberries</item>
      </ing>
      <ing>
        <amt>
          <qty>3</qty>
          <unit>cups</unit>
        </amt>
        <item>Sugar</item>
      </ing>
    </ingredients>
    <directions>
      <step>Put the strawberries in a pan.</step>
      <step>Add 1 cup of sugar.</step>
      <step>Bring to a boil and boil for 4 minutes.</step>
      <step>Add the second cup of sugar and boil again for 4 minutes.</step>
      <step>Then add the third cup of sugar and boil for 3 minutes.</step>
      <step>Remove from stove, cool, stir occasionally.</step>
      <step>Pour in jars and seal.</step>
    </directions>
  </recipe>
</recipeml>

Aufgabe:

  • Schreibe ein Programm, das ein XML-Dokument im RecipeML-Format ausgibt.

1.1.2. Prüfen, ob alle Bilder ein alt-Attribut haben ⭐

Bilder in HTML-Dokumenten sollten immer ein alt-Attribut haben.

Aufgabe:

1.1.3. Java-Objekte mit JAXB schreiben ⭐

JAXB vereinfacht den Zugriff auf XML-Dokumente, denn es erlaubt eine praktische Abbildung von einem Java-Objekt auf ein XML-Dokument und umgekehrt.

JAXB wurde in der Java 6 in die Standard Edition aufgenommen und in Java 11 wieder entfernt. Wir benötigen daher eine Dependency:

<dependency>
  <groupId>jakarta.xml.bind</groupId>
  <artifactId>jakarta.xml.bind-api</artifactId>
  <version>4.0.0</version>
</dependency>

<dependency>
  <groupId>org.glassfish.jaxb</groupId>
  <artifactId>jaxb-runtime</artifactId>
  <version>4.0.2</version>
  <scope>runtime</scope>
</dependency>

Aufgabe:

  • Schreibe JAXB-Beans, damit wir folgendes XML erzeugen können:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <ingredients>
        <ing>
            <amt>
                <qty>3</qty>
                <unit>cups</unit>
            </amt>
            <item>Sugar</item>
        </ing>
        <ing>
            <amt>
                <qty>3</qty>
                <unit>cups</unit>
            </amt>
        </ing>
    </ingredients>
  • Lege die Klassen Ingredients, Ing, Amt an.

  • Gibt den Klassen entsprechende Objektvariablen; es ist in Ordnung, wenn diese public sind.

  • Überlege, welche Annotation eingesetzt werden muss.

1.1.4. Witze einlesen und herzlich lachen ⭐⭐

Bonny Brain lacht auch über einfache Witze, wovon sie nie genug haben kann. Sie findet im Internet die Seite https://sv443.net/jokeapi/v2/joke/Any?format=xml, die ihr immer neue Witze liefert.

Das Format ist XML, das gut für den Datentransport ist, aber wir sind Java-Entwickler und wünschen uns alles in Objekten! Mit JAXB sollen die XML-Dateien eingelesen und in Java-Objekt konvertiert werden, sodass wir später eine individuelle Ausgabe entwickeln können.

Im ersten Schritt sollen aus einer XML-Schema-Datei JAXB-Beans automatisch generiert werden. Das Schema für die Joke-Seite ist wie folgt — keine Angst, man muss das nicht verstehen.

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="data">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:string" name="category" />
        <xs:element type="xs:string" name="type" />
        <xs:element name="flags">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:boolean" name="nsfw" />
              <xs:element type="xs:boolean" name="religious" />
              <xs:element type="xs:boolean" name="political" />
              <xs:element type="xs:boolean" name="racist" />
              <xs:element type="xs:boolean" name="sexist" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element type="xs:string" name="setup" />
        <xs:element type="xs:string" name="delivery" />
        <xs:element type="xs:int" name="id" />
        <xs:element type="xs:string" name="error" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Der Anbieter bietet kein Schema, daher ist es mithilfe von https://www.freeformatter.com/xsd-generator.html aus dem XML generiert.

Aufgabe:

  • Lade die XML-Schema-Definition unter http://tutego.de/download/jokes.xsd, und setze die Datei in das Maven-Verzeichnis /src/main/resources.

  • Ergänze die POM-Datei um folgendes Element:

    <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxb2-maven-plugin</artifactId>
        <version>3.1.0</version>
        <executions>
          <execution>
            <id>xjc</id>
            <goals>
              <goal>xjc</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <packageName>com.tutego.exercise.xml.joke</packageName>
          <sources>
            <source>src/main/resources/jokes.xsd</source>
          </sources>
          <generateEpisode>false</generateEpisode>
          <outputDirectory>${basedir}/src/main/java</outputDirectory>
          <clearOutputDir>false</clearOutputDir>
          <noGeneratedHeaderComments>true</noGeneratedHeaderComments>
          <locale>en</locale>
        </configuration>
      </plugin>
    </plugins>
    </build>

    Die Plugin-Sektion bindet org.codehaus.mojo:jaxb2-maven-plugin ein und konfiguriert es; alle Optionen sind unter https://www.mojohaus.org/jaxb2-maven-plugin/Documentation/v3.1.0/index.html erklärt.

  • Starte von der Kommandozeile mvn generate-sources. Es entstehen zwei Klassen im Paket com.tutego.exercise.xml.joke:

    • Data

    • ObjectFactory

  • Nutze JAXB, um von der URL https://sv443.net/jokeapi/v2/joke/Any?format=xml einen Witz zu beziehen und in ein Objekt zu konvertieren.

1.2. JSON

Java SE bietet von sich aus keine Unterstützung für JSON, aber es gibt zwei Standards aus dem Jakarta-EE-Projekt, die diese Unterstützung bieten: Jakarta JSON Processing (JSON-P) (https://jakarta.ee/specifications/jsonp/) und Jakarta JSON Binding (JSON-B) (https://jakarta.ee/specifications/jsonb/). Mit JSON-B können Java-Objekte in JSON abgebildet werden und umgekehrt, während JSON-P APIs für die Verarbeitung von JSON-Daten bereitstellt. Eine weitere beliebte Implementierung ist Jackson (https://github.com/FasterXML/jackson).

Um JSON-B zu nutzen, müssen wir sowohl die API als auch eine Implementierung in das POM unseres Projekts einbinden. Die Referenzimplementierung, Yasson, ist eine gute Wahl.

<dependency>
  <groupId>jakarta.json.bind</groupId>
  <artifactId>jakarta.json.bind-api</artifactId>
  <version>3.0.0</version>
</dependency>

<dependency>
  <groupId>org.eclipse</groupId>
  <artifactId>yasson</artifactId>
  <version>3.0.0</version>
  <scope>runtime</scope>
</dependency>

1.2.1. Hacker News JSON auswerten ⭐

Die Seite Hacker News (https://news.ycombinator.com/) wurde in Kapitel »Netzwerkprogrammierung«, kurz vorgestellt.

Die URL https://hacker-news.firebaseio.com/v0/item/24857356.json liefert ein JSON-Objekt der Nachricht mit der ID 24857356. Die Antwort sieht (formatiert und bei den kids etwas gekürzt) so aus:

{
   "by":"luu",
   "descendants":257,
   "id":24857356,
   "kids":[
      24858151,
      24857761,
      24858192,
      24858887
   ],
   "score":353,
   "time":1603370419,
   "title":"The physiological effects of slow breathing in the healthy human",
   "type":"story",
   "url":"https://breathe.ersjournals.com/content/13/4/298"
}

Mit JSON-B lässt sich dieses JSON in eine Map konvertieren:

Map map = JsonbBuilder.create().fromJson( quelle, Map.class );

Die quelle kann ein String, Reader oder InputStream sein.

Aufgabe:

  • Schreibe eine neue Methode Map<Object, Object> news(long id), die mithilfe von JSON-B das JSON-Dokument unter "https://hacker-news.firebaseio.com/v0/item/" + id + ".json" bezieht und in eine Map konvertiert und zurückliefert.

Beispiel:

  • news(24857356).get("title")"The physiological effects of slow breathing in the healthy human"

  • news(111111).get("title")null

1.2.2. Editor-Konfigurationen als JSON lesen und schreiben ⭐⭐

Die Entwickler arbeiten für Captain CiaoCiao an einem neuen Editor, und die Konfigurationen sollen in einer JSON-Datei gesichert werden.

Aufgabe:

  • Schreibe eine Klasse Settings, sodass sich die folgenden Konfigurationen abbilden lassen:

    {
      "editor" : {
        "cursorStyle" : "line",
        "folding" : true,
        "fontFamily" : [ "Consolas, 'Courier New', monospace" ],
        "fontSize" : 22,
        "fontWeight" : "normal"
      },
      "workbench" : {
        "colorTheme" : "Default Dark+"
      },
      "terminal" : {
        "integrated.unicodeVersion" : "11"
      }
    }
  • Die JSON-Datei lässt die Datentypen gut erkennen:

    • cursorStyle ist String, folding ist boolean, fontFamily ist ein Array oder List.

  • Wenn ein Attribut nicht gesetzt ist, also null ist, soll es nicht geschrieben werden.

  • Bei terminal sind die enthaltenen Schlüsselwerte unbekannt, sie sollen in einer Map<String, String> enthalten sein.

1.3. HTML

HTML ist eine wichtige Auszeichnungssprache. Die Java-Standardbibliothek bringt keine Unterstützung für HTML-Dokumente mit, sieht man einmal von dem ab, was die javax.swing.JEditorPane kann, nämlich HTML 3.2 und eine Teilmenge von CSS 1.0 darstellen.

Damit Java-Programme in der Lage sind, HTML-Dokumente korrekt und valide zu schreiben und einzulesen und Knoten auszulesen, müssen wir zu (Open-Source-)Bibliotheken greifen.

1.3.1. Mit jsoup Wikipedia-Bilder laden ⭐⭐

Die beliebte quelloffene Bibliothek jsoup (https://jsoup.org/) lädt den Inhalt von Webseiten und repräsentiert den Inhalt in einem Baum im Speicher.

Nimm folgende Dependency mit in das POM auf:

<dependency>
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.15.4</version>
</dependency>

Aufgabe:

1.4. Office-Dokumente

Microsoft Office steht weiterhin ganz oben, wenn es um Textverarbeitung und Tabellenkalkulation geht. Seit vielen Jahren ist das binäre Dateiformat wohlbekannt, und es gibt Java-Bibliotheken zum Lesen und Schreiben. Mittlerweile ist die Verarbeitung von Microsoft Office Dokumenten deutlich einfacher geworden, seitdem die Dokumente im Kern XML-Dokumente sind, die in einem ZIP-Archiv zusammengefasst werden. Die Unterstützung in Java ist sehr gut.

1.4.1. Word-Dateien mit Screenshots generieren ⭐⭐

Lies den Wikpedia-Eintrag zu POI: https://de.wikipedia.org/wiki/Apache_POI.

Aufgabe:

  1. Ergänze für Maven in der POM folgendes, damit Apache POI und die nötigen Abhängigkeiten für DOCX eingebunden werden:

    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.1.2</version>
    </dependency>
  2. Studiere den Quellcode von SimpleImages.java.

  3. Mit Java kann man Screenshots aufnehmen, und zwar so:

    private static byte[] getScreenCapture() throws AWTException, IOException {
      BufferedImage screenCapture = new Robot().createScreenCapture( SCREEN_SIZE );
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      ImageIO.write( screenCapture, "jpeg", os );
      return os.toByteArray();
    }
  4. Schreibe ein Java-Programm, das 20 Sekunden lang alle 5 Sekunden einen Screenshot macht und das Bild in das Word-Dokument hängt.

1.5. Archive

Dateien mit Metadaten fasst man in Archiven zusammen. Ein bekanntes und beliebtes Archivformat ist ZIP, was die Daten nicht nur zusammenfasst, sondern auch komprimiert. Viele Archivformate können die Dateien auch verschlüsselt speichern und Prüfsummen ablegen, sodass später Fehler in der Übertragung erkannt werden können.

Java bietet zwei Möglichkeiten zur Kompression: Ab Java 7 gibt es ein ZIP-File-System-Provider und schon seit Java 1.0 gibt es die Klassen ZipFile und ZipEntry.

1.5.1. Insektengeräusche aus dem ZIP-Archiv abspielen ⭐⭐

Bonny Brain horcht gerne den Geräuschen von Insekten und greift dabei auf die WAV-Sammlung von https://catalog.data.gov/dataset/bug-bytes-sound-library-stored-product-insect-pest-sounds zurück, wo verschieden Audio-Dateien in einem ZIP zum Download angeboten werden.

Play insect sounds from ZIP archive

Aufgabe:

  • Studiere die Dokumentation unter https://christian-schlichtherle.bitbucket.io/truezip/truezip-path/.

  • Nimm zwei Abhängigkeiten in die Maven-POM mit auf:

    <dependency>
      <groupId>de.schlichtherle.truezip</groupId>
      <artifactId>truezip-path</artifactId>
      <version>7.7.10</version>
    </dependency>
    
    <dependency>
      <groupId>de.schlichtherle.truezip</groupId>
      <artifactId>truezip-driver-zip</artifactId>
      <version>7.7.10</version>
    </dependency>
  • Lade das ZIP mit den Insektengeräuschen herunter, aber packe es nicht aus.

  • Baue für die ZIP-Datei ein TPath-Objekt auf.

  • Übertrage alle Dateinamen aus der ZIP-Datei in eine Liste: hier hilft Files.newDirectoryStream(…​).

  • Schreibe eine Endlosschleife, und

    • suche eine zufällige WAV-Datei aus,

    • öffne die zufällig ausgewählte Datei mit Files.newInputStream(…​), dekoriere sie mit einem BufferedInputStream und öffne einen AudioSystem.getAudioInputStream(…​). Spiele die WAV-Datei ab und greife auf folgenden Code zurück, wobei ais der AudioInputStream ist.

      Clip clip = AudioSystem.getClip();
      clip.open( ais );
      clip.start();
      TimeUnit.MICROSECONDS.sleep( clip.getMicrosecondLength() + 50 );
      clip.close();

      In Kapitel »Exceptions« hatten wir mit der javax.sound-API schon einmal gearbeitet.