Maps mittels GSON deserialisieren

GSON ist eine von Google entwickelte Java-Bibliothek, welche ursprünglich für den internen Gebrauch bei Google gedacht war. Mit dieser ist, neben vielen anderen Dingen, unter anderem die Serialisierung von Java-Objekten in JSON möglich. Gegeben sei für ein kleines Beispiel die Klasse FooObject:

public class FooObject {

    private int id;
    private String key;
    private String value;

    public FooObject(int id, String key, String value) {
        this.id = id;
        this.key = key;
        this.value = value;
    }

    public int getId() {
        return id;
    }

    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }
}

Die Klasse besteht aus drei internen Variablen, welche eine Id, einen Key und einen Value halten. Daneben existieren ein Konstruktor und drei Getter-Methoden für die Rückgabe der internen Variablen. Mithilfe von GSON kann eine Instanz der Klasse einfach zu JSON serialisiert werden:

Gson gson = new Gson();
FooObject fooObject = new FooObject(1, "keyA", "valueA");
String jsonFooObject = gson.toJson(fooObject);

Heraus kommt bei dieser Serialisierung folgendes JSON-Objekt:

{
   "id":1,
   "key":"keyA",
   "value":"valueA"
}

Auch die Deserialisierung des Objektes mittels GSON ist kein Problem:

fooObject = gson.fromJson(jsonFooObject, FooObject.class);

Interessanter und komplizierter wird es, wenn Maps mittels GSON serialisiert werden sollen:

Map values = new HashMap<>();
values.put("A", new FooObject(1, "keyA", "valueA"));
values.put("B", new FooObject(2, "keyB", "valueB"));
values.put("C", new FooObject(3, "keyC", "valueC"));

Die Serialisierung gestaltet sich noch einfach:

String json = gson.toJson(values);

Bei der Deserialisierung mittels:

Map map = gson.fromJson(json, Map.class);

erhält der Nutzer allerdings eine LinkedTreeMap. Auf die Werte der Map kann durchaus zugegriffen werden:

String value = map.get("A").get("key");

Allerdings schlägt die Umwandlung in ein Objekt vom Typ FooObject fehl:

FooObject fooObjectB = map.get("A");

Stattdessen erhält der Nutzer eine ClassCastException:

Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to org.example.console.FooObject

Damit der Cast gelingt muss bei der Deserialisierung der Map anders gearbeitet werden:

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;

...

Type type = new TypeToken>(){}.getType();
Map map = gson.fromJson(json, type);

Damit kann die Map mit dem FooObject normal genutzt werden. Mithilfe des TypeTokens kann ein generischer Typ repräsentiert und somit für die Deserialisierung mittels GSON eingesetzt werden.

Enumerationen unter Java mit Eigenschaften ausstatten

Im Gegensatz zu vielen anderen Programmiersprachen sind Enumerationen unter Java ein wenig anders gestrickt. Wird eine Enumerationen dort kompiliert entsteht als Ergebnis eine Klasse. Dieser Umstand kann für interessante Dinge genutzt werden. Während in C# der Enum:

public enum Unit {
    PASCAL,
    PSI
}

eine reine Enumeration, mit zwei Werten, darstellt, kann die Enumeration unter Java mit zusätzlicher Funktionalität ausgestattet werden:

public enum Unit {

    PASCAL("Pascal", 1),
    PSI("Pound-force per square inch", 6.895);

    Unit(String name, double conversionFactor) {
        this.name = name;
        this.conversionFactor = conversionFactor;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getConversionFactor() {
        return conversionFactor;
    }

    public void setConversionFactor(double conversionFactor) {
        this.conversionFactor = conversionFactor;
    }

    private String name;
    private double conversionFactor;
}

In diesem Fall erhält die Enumeration zwei zusätzliche Eigenschaften, einen Namen und einen Faktor zur Umrechnung in Pascal. Bei der Definition der einzelnen Werte der Enumeration werden diese zusätzlichen Eigenschaften über den Konstruktor übergeben. In der Nutzung sieht das Ganze dann wie folgt aus:

Unit unit = Unit.PSI;

System.out.println(unit.getName());
System.out.println("Conversion factor to Pascal: " + unit.getConversionFactor());

Mit dieser Herangehensweise lassen sich Enumerationen unter Java einfach mit dazugehörigen Metadaten bzw. zusätzlichen Informationen anreichern.