Skip to content

GSON

Wstęp

Tak jak wspomniałem w poprzednim poście tym razem zajmiemy się bardziej praktyczną stroną wykorzystania formatu JSON. W tym przykładzie wykorzystam bibliotekę Gson stworzoną przez Google.

Gson jest biblioteką, którą można wykorzystać w celu łatwej konwersji obiektów Javy do formatu JSON, a także oczywiście odwrotnie –  w celu konwersji formatu JSON do obiektów Javy. Można również wykorzystywać ją do serializacji obiektów bez dostępu do ich kodu źródłowego. Oryginalnie Gson został stworzony w celu funkcjonowania wewnątrz firmy Google, gdzie w dalszym ciągu jest wykorzystywany w ogromnej liczbie projektów. Na chwilę obecną działa na zasadach open-source i jest wykorzystywany w wielu dużych projektach.

Główne założenia

  • Zapewnia łatwy w użyciu mechanizm przypominający metodę toString() i konstruktor (metoda factory) konwertujący obiekty Javy do formatu JSON i vice-versa
  • Umożliwia konwersję niemodyfikowalnych obiektów do formatu JSON i vice-versa
  • Dopuszcza własne reprezentacje obiektów
  • Obsługuje dowolnie złożone obiekty
  • Generuje kompaktowe i łatwe w odczytaniu wyjście w formacie JSON

Getting started

W pierwszej kolejności oczywiście potrzebujemy samą bibliotekę. W przypadku Gradle lub Maven możemy wykorzystać następujące zależności:

Gradle

compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'

Maven

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.0</version>
</dependency>

Osobiście do konwersji wykorzystuję utworzony obiekt klasy Gson. Jest jeszcze klasa GsonBuilder, umożliwiająca tworzenie instancji Gson z różnymi modyfikacjami, takimi jak np. obsługa kontroli wersji. Nie będzie ona omawiana w ramach tego wpisu.

W celu stworzenia obiektu klasy Gson:

        Gson gson = new Gson();
        
        // Serialization
        String json = gson.toJson(T object, T.class);
        
        // Deserialization
        T deserializedObject = gson.fromJson(json, T.class);

Metoda toJson(Object object) służy do serializacji obiektu – czyli jego zapisu do formatu JSON. Metoda fromJson(String json, Class or Type of object) analogicznie wykonuje operację odwrotną.

Serializacja

Obiekty (w tym typy prymitywne, tablice)

Załóżmy, że mamy nieco zmodyfikowaną wersję klasy z poprzedniego wpisu na temat formatu JSON:

public class Pizza {
    private String name;
    private PizzaSize size;
    private boolean isVegetarian;
    private transient int price;

    public Pizza(String name, PizzaSize size, boolean isVegetarian, int price) {
        this.name = name;
        this.size = size;
        this.isVegetarian = isVegetarian;
        this.price = price;
    }
}

PizzaSize jest typem wyliczeniowym i ma następującą postać:

public enum PizzaSize {
    SMALL (25),
    MEDIUM (32),
    BIG (45);

    private final int diagonal;

    PizzaSize(int diagonal) {
        this.diagonal = diagonal;
    }
}

Przeprowadźmy więc serializację do formatu JSON:

public class Main {
    public static void main(String[] args) {
        Pizza carbonara = new Pizza("Carbonara", PizzaSize.SMALL, false, 23);

        Gson gson = new Gson();
        String carbonaraJson = gson.toJson(carbonara);
        
        System.out.println(carbonaraJson);
    }
}

Dostaniemy następujące wyjście:

{
   "name":"Carbonara",
   "size":"SMALL",
   "isVegetarian":false
}

Dla większości przypadków sposób serializacji wygląda identycznie.

Typy generyczne

Załóżmy, że mamy następującą klasę (pomijam bezsensowność stosowania typu generycznego w tym przypadku):

public class MysteriousPizza<T> extends Pizza {
    private T mysteriousIngredient;

    public MysteriousPizza(String name, PizzaSize size, boolean isVegetarian, 
                           int price, T mysteriousIngredient) {
        super(name, size, isVegetarian, price);
        this.mysteriousIngredient = mysteriousIngredient;
    }
}

W celu serializacji jej obiektów musimy zadbać o zgodność typów. Gdybyśmy metodzie fromJson() lub toJson() podali jako drugi argument po prostu  MysteriousPizza.class otrzymalibyśmy zwykłe MysteriousPizza, a nie MysteriousPizza<T>!

Załóżmy, że tajemniczym składnikiem może być obiekt takiej klasy:

public class SpicySauce {
    private int spice;

    public SpicySauce(int spice) {
        this.spice = spice;
    }
}

Proces serializacji może wyglądać następująco:

public class Main {
    public static void main(String[] args) {
        Gson gson = new Gson();

        MysteriousPizza<SpicySauce> mysteriousPizza = new MysteriousPizza<>(
                "Carbonara", PizzaSize.SMALL, false,
                23, new SpicySauce(10));
        Type mysteriousType = new TypeToken<MysteriousPizza<SpicySauce>>() {}.getType();

        String json = gson.toJson(mysteriousPizza, mysteriousType);
        System.out.println(json);
    }
}

Dostaniemy następujące wyjście:

{
   "mysteriousIngredient":{
      "spice":10
   },
   "name":"Carbonara",
   "size":"SMALL",
   "isVegetarian":false
}

Deserializacja

Obiekty (w tym typy prymitywne, tablice)

Mamy następujące wejście:

{
   "name":"Carbonara",
   "size":"SMALL",
   "isVegetarian":false
}

W celu deserializacji formatu JSON do postaci obiektu Javy możemy napisać następujący kod:

public class Main {
    public static void main(String[] args) {
        Gson gson = new Gson();

        String input = readJson("src/json_files/carbonara.json");
        Pizza carbonara = gson.fromJson(input, Pizza.class);
    }
}

Metoda readJson() jest niczym innym jak zwykłą metodą wczytującą plik tekstowy i zwracającą go w postaci łańcucha znaków:

    private static String readJson(String path) {
        StringBuilder builder = new StringBuilder();

        String text;
        try {
            BufferedReader reader = new BufferedReader(new FileReader(path));
            while ((text = reader.readLine()) != null) {
                builder.append(text);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return builder.toString();
    }

Dla większości przypadków sytuacja będzie wyglądać identycznie! Również wtedy, gdy polem klasy będzie obiekt klasy wewnętrznej (oraz zagnieżdżonej) nieposiadającej konstruktora.

Typy generyczne

Deserializacji wygląda analogicznie do serializacji. Tak samo musimy pamiętać o użyciu odpowiedniego tokenu.

Mamy następujące wejście:

{
   "mysteriousIngredient":{
      "spice":10
   },
   "name":"Carbonara",
   "size":"SMALL",
   "isVegetarian":false
}

W celu deserializacji:

    public static void main(String[] args) {
        Gson gson = new Gson();
        String json = readJson("src/json_files/generic_pizza.json");

        Type mysteriousType = new TypeToken<MysteriousPizza<SpicySauce>>() {}.getType();
        MysteriousPizza<SpicySauce> mysteriousPizza = gson.fromJson(json, mysteriousType);
    }

Podsumowanie

Pomimo, że wpis ten zawiera podstawy użytkowania biblioteki Gson, widać raczej wyraźnie, że obsługa całości jest bardzo prosta i w przypadku większości elementarnych, codziennych zastosowań odpowiednia implementacja nie będzie stanowić żadnego problemu.


Źródła:

Facebooktwitterredditlinkedinmail
Published inJavaNarzędziaProgramowanie