JSON
Willemers Informatik-Ecke
Das Format JSON hat sich in den letzten Jahren als schlanke Alternative für XML verbreitet.

Das JSON-Format

Aus Arrays und Objekten lassen sich die Java-Datenstrukturen nachbilden. Als Beispiel betrachten wir eine Person mit der Neigung zu falschen Angaben.
{ "name": "Arnold Willemer", "alter": "12", "ort": "Norgaardholz" }
Ein Array nimmt mehrere gleichartige Elemente auf. Das Array erhält einen Namen, die Elemente allerdings enthalten lediglich Werte. Als Beispiel sehen Sie die großartigsten Bands dieses Planeten.
[ "The Beatles", "ABBA", "Beatwatt" ]
Ein Objekt kann als Wert wiederum ein Objekt oder ein Array verwenden. Dadurch sind beliebige Verschachtelungen möglich.
{
  "name": "Arnold Willemer",
  "instrumente": [ "Gitarre", "Mundharmonika", "Kazoo" ]
  "ort":  "Norgaardholz",
  "sprachen": [
    { "sprache": "deutsch", "kenntnis": "gut" }
    { "sprache": "englisch", "kenntnis": "gut" }
    { "sprache": "plattdeutsch", "kenntnis": "schlecht" }
  ]
}
Ein Syntaxdiagramm auf http://json.org stellt die komplette Beschreibung dar.

JSON in Java

Sie benötigen eine Bibliothek. Tatsächlich gibt es sogar mehrere Implementationen von JSON. Für die Beispiele habe ich die Org.Json verwendet. Eine passende Jar-Datei können Sie unter folgender URL herunterladen.

http://mvnrepository.com/artifact/org.json/json

Um Daten in JSON zu bearbeiten, legen Sie ein Objekt der Klasse JSONObject an und fügen die Elemente mit der Methode put hinzu. Die Methode erwartet als ersten Parameter den Schlüssel. Der zweite Parameter erwartet den Wert als Referenz. Darum müssen primitive Datentypen mit einem Wrapper umgeben werden.

import org.json.*;
public class JsonBau {
    public static void main(String[] args) {
        JSONObject jobj = new JSONObject();
        jobj.put("name", "Arnold Willemer");
        jobj.put("zahl", new Integer(100));
        jobj.put("gehalt", new Double(1234.56));
        jobj.put("istklug", new Boolean(true));
        String jsonString = jobj.toString();
        System.out.println(jsonString);
    }
}
Listing: Erzeugen eines JSON-Objekts

Das Auslesen oder Parsen eines JSON-Strings erfolgt dadurch, dass man dem Konstruktor von JSONObject als Parameter diesen String übergibt. Das Auslesen eines Wertes erfolgt mit der typangepassten get-Methode. Als erster Parameter muss der Schlüssel angegeben werden.

JSONObject jobj = new JSONObject(jsonString);
String name     = jobj.getString("name");
int zahl        = jobj.getInt("zahl");
double gehalt   = jobj.getDouble("gehalt");
boolean istklug = jobj.getBoolean("istklug");
Listing: Auslesen eines JSON-Objekts

Arrays

Um ein Array zu behandeln, gibt es die Klasse JSONArray. Dennoch benötigen Sie hier zunächst ein JSONObject, um für das Array einen Schlüssel angeben zu können.

Beim Erzeugen der JSON-Struktur verwenden Sie das Array als Parameter für den Konstruktor von JSONArray. Nun erzeugen Sie ein JSONObject. Ihm fügen Sie das JSONArray-Objekt über die Methode put hinzu, und geben dabei als ersten Parameter den Namen des Arrays an.

import org.json.*;
public class JsonArray {
    public static void main(String[] args) {
        int[] quadrate = { 1, 4, 9, 16, 25 };
        JSONArray qarr = new JSONArray(quadrate);
        JSONObject qobj = new JSONObject();
        qobj.put("quadrate", qarr);
        String js = qobj.toString();
        System.out.println(js);
        // ...
Listing: Erzeugen eines JSON-Arrays

Beim Auslesen des JSON-Strings mit einem Array erzeugen Sie wie zuvor ein JSONObject, dem Sie als Parameter den JSON-String übergeben. Sie geben den Schlüssel des Arrays an, um mit der Methode getJSONArray das JSONArray zu bekommen. Darüber erzeugen Sie eine Schleife. Mit der Methode length erhalten Sie die Anzahl der Elemente. Innerhalb der Schleife können Sie die Elemente mit der typspezifischen get-Methode ermitteln. Als Parameter übergeben Sie den Index.

        // ...
        JSONObject obj = new JSONObject(js);
        JSONArray jarr = obj.getJSONArray("quadrate");
        int neuquad[] = new int[quadrate.length];
        for (int i = 0; i < jarr.length(); i++) {
            neuquad[i] = jarr.getInt(i);
        }
        for (int qzahl : neuquad) {
            System.out.println(qzahl);
        }
    }
}
Listing: Auslesen eines JSON-Arrays

Klassen sorgen selbst für JSON

Die JSON-Strings haben ihr Gegenstück typischerweise in einer Java-Klasse. Darum ist der Gedanke naheliegend, dass jede Klasse eine Methode zum Erzeugen des JSON-Strings und eine Methode zum Einlesen eines JSON-Strings enthält. Das Beispiel in Listing verwendet eine eigene Methode, um ein JSONObject zu erstellen.
public class Sprache {
    String sprache = "";
    String kenntnis = "";
    public JSONObject toJSONObj() {
        JSONObject obj = new JSONObject();
        obj.put("sprache", this.sprache);
        obj.put("kenntnis", this.kenntnis);
        return obj;
    }
}
Listing: Die Klasse Sprache mit Methode toJSONObj

Das bleibt Ihnen natürlich unbenommen. Etwas eleganter ist es jedoch, wenn Sie zum Erzeugen des JSONObject ein Objekt der eigenen Klasse dem Konstruktor als Parameter übergeben. In dem Moment wird der Konstruktor automatisch aus der Klasse ein JSON-Objekt erzeugen. Dabei geht er folgendermaßen vor:

Beim Beispiel in Listing kann ein Objekt der Klasse automatisch in ein JSONObject überführt werden, indem es als Parameter des Konstruktors übergeben wird, da es die entsprechenden get-Methoden implementiert.
public class Sprache {
    String sprache = "";
    String kenntnis = "";
    public String getSprache() {
        return sprache;
    }
    public String getKenntnis() {
        return kenntnis;
    }
}
Listing: Die Klasse benötigt keine JSON-Methode

Nun kann wird das JSONObject erzeugt, indem das Objekt der Klasse Sprache als Parameter dient.

Sprache sprache = new Sprache();
...
JSONObject personobj = new JSONObject(sprache);

Die Klassen, die in JSON überführt werden und zurück, sind häufig nicht ganz so trivial wie die oben angeführten Beispiele. Darum zeigt das Listing den Umgang mit einer Klasse Person, die eine ArrayList für Strings und eines für die Klasse Sprache verwendet.

Um ein JSONObject auszulesen wurde nicht eine Methode verwendet, sondern ein Konstruktor mit dem entsprechenden Parameter.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONObject;

public class JsonSpielerei {

    public class Sprache {
        String sprache = "";
        String kenntnis = "";

        public String getSprache() {
            return sprache;
        }

        public String getKenntnis() {
            return kenntnis;
        }

        public Sprache(String pSprache, String pKenntnis) {
            sprache = pSprache;
            kenntnis = pKenntnis;
        }

        public Sprache(JSONObject json) {
            this.sprache = json.getString("sprache");
            this.kenntnis = json.getString("kenntnis");
        }
    }

    public class Person {
        String name = "";
        ArrayList<String> instrumente = new ArrayList<String>();
        int alter = 0;
        ArrayList<Sprache> sprachen = new ArrayList<Sprache>();

        public String getName() {
            return name;
        }

        public ArrayList<String> getInstrumente() {
            return instrumente;
        }

        public int getAlter() {
            return alter;
        }

        public ArrayList<Sprache> getSprachen() {
            return sprachen;
        }

        public Person() {
        }

       public Person(JSONObject jsonObject) {
            this.name = jsonObject.getString("name");
            this.alter = jsonObject.getInt("alter");
            JSONArray instrJson = jsonObject.getJSONArray("instrumente");
            Iterator<Object> iterator = instrJson.iterator();
            while (iterator.hasNext()) {
                this.instrumente.add((String) iterator.next());
            }
            JSONArray sprachenJson = jsonObject.getJSONArray("sprachen");
            for (int i = 0; i < sprachenJson.length(); i++) {
                JSONObject jsonSprache = sprachenJson.getJSONObject(i);
                Sprache sprache = new Sprache(jsonSprache);
                sprachen.add(sprache);
            }
        }
    }
    // ...
Listing: Ein größeres Beispiel

Nun soll das alles getestet werden. Dazu wurde der Klasse eine main-Methode zur Seite gestellt, die den Konstruktor der Klasse ruft, um in diesem den Test durchzuführen.

Die Methode zeigt auch gleich, wie man einen JSON-String in einer Datei ablegt und ihn dort wieder ausliest, da dies schließlich eine recht häufige Anwendung von JSON ist.

    // ...
    public JsonSpielerei() {
        Person person = new Person();
        // wir verändern nun ein paar Elemente der Person
        person.name = "Arnold Willemer";
        person.instrumente.add("Gitarre");
        person.instrumente.add("Mundharmonika");
        person.alter = 13;
        person.sprachen.add(new Sprache("deutsch", "toll"));
        person.sprachen.add(new Sprache("englisch", "naja"));
        JSONObject personobj = new JSONObject(person);
        System.out.println(personobj.toString());
        // Die JSON-Struktur wird in eine Datei geschrieben
        try {
            FileWriter file = new FileWriter("/home/arnold/jsontest.txt");
            file.write(personobj.toString());
            file.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // JSON-Struktur aus Datei lesen und Duplikat erzeugen
        try {
            BufferedReader reader = new BufferedReader(
                  new FileReader("/home/arnold/jsontest.txt"));
            String zeile = "";
            String jsonString = "";
            while ((zeile = reader.readLine()) != null) {
                jsonString += zeile;
            }
            reader.close();
            Person duplikat = new Person(new JSONObject(jsonString));
            // Zum Test lesen wir die neue Person aus.
            JSONObject dupobj = new JSONObject(duplikat);
            System.out.println(dupobj.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new JsonSpielerei();
    }
}
Listing: Test -JSON in Datei schreiben und wieder auslesen

Die GSON-Bibliothek

Die GSON-Bibliothek unterstützt das automatische Konvertieren von Objekten in JSON und zurück.

Man kann GSON unter der Adresse repo1.maven.org/maven2/com/google/code/gson/gson herunterladen. Darunter findet man eine Liste der Versionsnummern und darunter eine Datei namens gson-x.y.z.jar. Diese wird dem Projekt hinzugefügt und in den Build-Path eingebunden.

JSON-String in Objekt überführen

import com.google.gson.Gson;

    // ...
String jsonString = // ...
Gson gson = new Gson();
Person[] liste = gson.fromJson(jsonString.toString() + "", Person[].class);
for (Person p : liste) {
    System.out.println(p.getName());
}

Objekt in einen JSON-String überführen

import com.google.gson.Gson;

    // ...
Person person = // ...
Gson gson = new Gson();
String jsonString = gson.toJson(person);

Datum

Leider kennt JavaScript keinen Standard für einen Datumstyp und so geht es JSON auch. Die Übertragung von Date stellt ein Problem dar, dass man am einfachsten dadurch löst, dass man es nicht tut, sondern die Zahl der Millisekunden ab dem 1.1.1970 als long-Wert überträgt. Der Konstruktor von Date akzeptiert diesen Wert, um ein neues Datum zu erzeugen. Die Methode toTime() ermittelt aus einem Date-Objekt die Anzahl der Millisekunden.

Falls Sie das Übertragungsformat selbst bestimmen können, wäre die Verwendung von XML eine Alternative, das mit der Übertragung eines Datums kein Problem hat. Eine vergleichbare Funktionalität wie GSON bietet JAXB.