Android: Utiliser Gson pour faciliter l’utilisation du Json

Partager cet article

Temps estimé pour la lecture de cet article : 38 min

Introduction à Gson

Gson est une librairie Java proposé par Google permettant de convertir des objets Java en Json. Elle peut également faire l’inverse, c’est-à-dire convertir un résultat Json directement dans l’objet Java correspondant.

android-gson-introduction

D’autres librairies, comme moshi proposent ce genre d’outil, toutefois bien souvent, elles nécessitent d’utiliser un système d’annotations, hors parfois vous n’avez pas accès au code avec Gson pas de soucis !

La librarie se veut vraiment simple d’accès, notamment en proposant principalement deux méthodes :

  • toJson(), cette méthode permet de créer un objet à partir d’un résultat Json et du type de retour désiré
  • fromJson(), celle-ci fait exactement l’inverse elle permet de convertir un objet en json

Nouvelle instance de Gson

Il faut savoir que lors de l’initialisation d’un objet Gson, il est possible de préciser un builder qui va permettre à Gson de conserver les valeurs nulles lors de la sérialisation ou désérialisation. En effet sinon par défaut, gson ne les conservera pas donc si vous aviez quelque chose du genre :

HashMap<String, Object> map = new HashMap<>();
map.put("id", null);
String result = gson.toJson(map);
System.out.println(result);  // {}

En retour, vous allez à voir un json vide ! Pour éviter ça, il faut donc préciser lors de la création de l’objet gson ceci :

final Gson gson = new GsonBuilder().serializeNulls().create();

Et là on aurait en résultat :

{
"id" : null
}

De même, certains caractères sont échappés par défaut, comme par exemple les symboles <, > ou =. Ils sont alors remplacés par leur encodage en UTF-8. Si on veut éviter ça, il suffit de préciser l’option disableHtmlEscaping(), ce qui nous donnerait par exemple :

final Gson gson = new GsonBuilder()
.serializeNulls()
.disableHtmlEscaping()
.create();

Utiliser Gson pour rendre un json bien formatté

Une des forces de Gson, c’est qu’il propose également la fonction setPrettyPrinting, qui va permettre de faire un rendu formatté d’un objet json, donc si on avait par exemple ce genre de retour json :

{ "games" : [ { "id" : "15", "name" : "final fantasy X" }, { "id" : "85", "name" : "God of War" } ] }

On va pouvoir formatté le tout, en utilisant une fonction de ce style là :

public static String jsonToPrettyFormat(String jsonString) {
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(jsonString).getAsJsonObject();
Gson gson = new GsonBuilder()
.serializeNulls()
.disableHtmlEscaping()
.setPrettyPrinting()
.create();
return gson.toJson(json);
}

Et, voici maintenant notre retour formatté !

{
"games": [
{
"id": "15",
"name": "final fantasy X"
},
{
"id": "85",
"name": "God of War"
}
]
}

Ça donne envie, non :3 ? Et ce n’est que le début 😉 !

Mise en place de la librairie

Grâce à gradle, il est très simple de mettre en place la librairie, il vous suffit d’ouvrir le fichier build.gradle de votre application et d’y ajouter le lien vers le dépôt maven grâce à la commande compile dans les dependencies. Votre fichier devrait ressembler à quelque chose du genre:

apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "fr.borntocode.gsontests"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
// On rajoute ici, notre instruction
compile 'com.google.code.gson:gson:2.7'
}

Ici, lors de l’article la dernière version de Gson était le 2.7, si vous voulez vérifier si une nouvelle version est disponible, il vous suffit de vous rendre sur le repository de maven ou bien sur directement sur le github du projet.

Cas pratique: des livres

android-gson-example

Nous allons maintenant montrer l’utilisation de la librairie à travers plusieurs exemples s’appuyant sur une application de gestion de livres qui serait basé sur du Json. Voici, donc un objet Book qui va permettre de représenter un livre.

public class Book {
private String id;
private String name;
private String author;
private String genre;
private int numpages;
private String releaseDate;
private String cover;
public Book() {}
public Book(String id, String name, String author, String genre, int numpages, String releaseDate, String cover) {
this.id = id;
this.name = name;
this.author = author;
this.genre = genre;
this.numpages = numpages;
this.releaseDate = releaseDate;
this.cover = cover;
}
...
@Override
public String toString() {
return "Book [ " +
"id='" + id + '\'' +
", name='" + name + '\'' +
", author='" + author + '\'' +
", genre='" + genre + '\'' +
", numpages=" + numpages +
", cover=" + cover +
", releaseDate='" + releaseDate + '\'' +
']';
}
}

Et voici, un fichier Json de base contenant déjà quelques livres (comment ça, il n’y a que de la SF :3) :

[
{
"id": "01fsEF",
"name": "1984",
"author": "George Orwell",
"genre": "Fiction dystopique",
"numpages": 376,
"release_date": "1949",
"cover": "1984.png"
},
{
"id": "3H1J0n",
"name": "Le Meilleur des mondes",
"author": "Aldous Huxley",
"genre": "Science-fiction",
"numpages": 285,
"release_date": "1932",
"cover": "meilleur-des-mondes.jpg"
},
{
"id": "MbtsI7",
"name": "Malevil",
"author": "Robert Merle",
"genre": "Littératures de l'imaginaire",
"numpages": 541,
"release_date": "1972",
"cover": "malevil.jpg"
},
{
"id": "AIqWon",
"name": "Fondation",
"author": "Isaac Asimov",
"genre": "Science-fiction",
"numpages": 256,
"release_date": "1951",
"cover": "fondation.jpg"
},
{
"id": "7d8vm8",
"name": "Time Riders, tome 1",
"author": "Alex Scarrow",
"genre": "Science fiction",
"numpages": 428,
"release_date": "2012",
"cover": "time-riders.jpg"
}
]

Désérialiser du Json avec Gson

Admettons maintenant que nous aimerions récupérer l’ensemble de ces livres, on pourrait le faire en s’aidant des classes de base, comme dans ce tutoriel mais c’est là que réside tout l’intérêt de Gson, au lieu de s’embêter à écrire 40 lignes de code pour pouvoir gérer un type d’objet uniquement, on va pouvoir faire tout ça, en deux lignes. Voyez plutôt :

import java.lang.reflect.Type;
....
Type listType = new TypeToken<ArrayList<Book>>(){}.getType();
ArrayList<Book> books = new Gson().fromJson(jsonBooks, listType);
for (Book book : books) {
Log.e("MainActivity", book.toString());
}

Ici, ce qui devrait vous effrayer c’est la classe TypeToken. Elle va permettre à la librairie de connaitre le type de retour de notre liste mais pourquoi diable a-t-on besoin de ça me direz-vous ?

Il faut savoir que lors de la phase de compilation, une étape appelée « type erasure » a lieu, suite à cette dernière java ne va pas conserver l’information sur le type des objets génériques. Ainsi, si vous aviez une ArrayList alors vous ne pourrez pas déterminer le type de cette dernière à l’exécution. Cependant grâce à la refléxion, il va être possible de récupérer ce type notamment grâce à la classe ParameterizedType. C’est ce genre d’outil que va utiliser la classe TypeToken afin de connaître le type de la liste même à l’exécution et c’est aussi pourquoi, nous avons besoin de cette classe.

Sérialiser du Json avec Gson

Une fois que vous maitriser l’une des méthodes l’autre devient facile, ici on a donc notre liste d’objets que l’on souhaite convertir en json, il va nous suffire de passer la liste et son type afin de créer le résultat désiré. La fonction toJson va alors renvoyer la chaine finale.

Type listType = new TypeToken<ArrayList<Book>>(){}.getType();
String jsonResult = gson.toJson(list, listType);

Vous allez voir, on prend vite goût à cette simplicité ;).

Les annotations

android-gson-annotations

Gson propose un système d’annotations pour permettre de personnaliser les opérations de sérialisation/désérialisation, on retrouve notamment:

  • @Expose : permet de préciser si un champ doit être utilisé ou non lors des opérations de sérialisation et de désérialisation
  • @SerializedName : permet de préciser le nom du champ qui sera utilisé lors des opérations de sérialisation/désérialisation
  • @Since : permet de définir à partir de quelle version le champ ou la classe doit être prise en compte
  • @Until : permet de définir jusqu’à quelle version le champ ou la classe doit être prise en compte

Regardons sur l’exemple de clase Book, les différentes annotations possibles :

public class Book {
private String id;
private String name;
private String author;
@SerializedName("g")
private String genre;
@Since(1.0)
private int numpages;
@Expose(serialize = false, deserialize = false)
private String releaseDate;
@Until(1.0)
private String cover;
public Book() {}
public Book(String id, String name, String author, String genre, int numpages, String releaseDate, String cover) {
this.id = id;
this.name = name;
this.author = author;
this.genre = genre;
this.numpages = numpages;
this.releaseDate = releaseDate;
this.cover = cover;
}
...
}

Commençons par l’annotation la plus utile selon moi : SerializedName, cette dernière va tout simplement permettre de renommer le champ afin qu’il corresponde au nom souhaité lors de la transformation en json, cela va permettre par exemple de réduire la taille du fichier final, un must have. L’annotation Expose comme vous pouvez le voir, il est possible de contrôler la serialisation ou deserialisation d’un champ, ainsi, ici notre attribut releaseDate, ne serait jamais pris en compte par Gson. Les champs Until et Since vont permettre de contrôler la visibilité d’un objet en fonction du numéro de version de la release par exemple.

Le mot de la fin

Afin de vous aider et de vous fournir un exemple concret, un dépôt est disponible sur github. Vous y trouverez une petite application permettant d’ajouter et de visualiser des livres, rien de très extraordinaire, have fun les amis.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *