:: Enseignements :: Master :: M1 :: 2018-2019 :: Java Avancé ::
[LOGO]

JSON, réflexion et annotations


Ce TD à pour but de comprendre l'intérêt de la réflexion et des annotations.

Exercice 1 - JSON Encoder

On souhaite écrire un code qui permet d'afficher un objet au format JSON, par exemple pour la classe Person:

On peut écrire la méthode toJSON qui prend en paramètre une Person et renvoie une chaîne de caractères au format JSON:
public static String toJSON(Person person) {
    return
        "{\n" +
        "  \"firstName\": \"" + person.getFirstName() + "\"\n" +
        "  \"lastName\": \"" + person.getLastName() + "\"\n" +
        "}\n";
}
Mais supposons que nous ayons envie d'écrire un méthode toJSON qui prend en paramètre un Alien et qui fait la même chose, nous allons dupliquer du code (dans la classe Main).



Et si l'on doit dupliquer le code de toJSON à chaque fois que l'on veut transformer en JSON une nouvelle classe, c'est embêtant...
A kitten dies each time you duplicate a bug !
Pour éviter l'hécatombe, on se propose d'écrire une seule méthode toJSON prenant un Object en paramètre et utilisant la réflexion (reflection en anglais) pour trouver les propriétés à écrire au format JSON.

  1. Écrire une méthode toJSON qui prend en paramètre un Object, utilise la réflexion pour accéder à l'ensemble des méthodes publiques de la classe de l'objet (java.lang.Class.getMethods), sélectionne les getters, puis affiche les couples nom de propriété, valeur associée.
    Le nom d'une propriété peut s'obtenir à partir du nom du getter en utilisant la fonction suivante (en supposant que votre getter s'appelle bien getSomething):
         private static String propertyName(String name) {
           return Character.toLowerCase(name.charAt(3)) + name.substring(4);
         }
        

    Note: il est possible (recommandé) d'écrire la méthode en utilisant un Stream.
    Note 2: faîtes attention à gérer correctement les exceptions lors de l'invocation de méthode (surtout InvocationTargetException).
  2. En fait, la méthode précédente n'affiche pas uniquement le résultat de getFirstName et getLastName car il existe une méthode getClass qui commence aussi par le préfixe "get". Pour éviter cela, on va plutôt marquer les méthodes qui feront partie de l'affichage JSON. À cette fin, on se propose d'utiliser une annotation.
    Déclarez l'annotation JSONProperty visible à l'exécution et permettant d'annoter des méthodes, puis modifiez le code de toJSON pour n'utiliser que les propriétés issues de méthodes marquées par l'annotation JSONProperty.
  3. En fait, une propriété JSON peut contenir des caractères comme le '-' qui ne sont pas des caractères valides dans un nom de méthode Java.
    Faîtes en sorte que l'on puisse utiliser l'annotation JSONProperty sans rien, et dans ce cas le nom de la méthode sera utilisée, mais que l'on puisse aussi utiliser l'annotation JSONProperty avec un nom. Ce nom sera alors utilisé au lieu du nom de la méthode.
    Rappel: la valeur par défaut d'un attribut d'une annotation ne peut pas être null.
  4. En fait, l'appel à getMethods est lent; regardez la signature de cette méthode et expliquez pourquoi...
    Regardez la méthode java.lang.reflect.Method.setAccessible et indiquez pourquoi l'appel à getMethods ne peut pas être accéléré.
  5. Nous allons donc limiter les appels à getMethods en stockant le résultat de getMethods dans un cache pour éviter de faire l'appel à chaque fois qu'on utilise toJSON.
    Utilisez la classe java.lang.ClassValue pour mettre en cache le résultat d'un appel à getMethods pour une classe donnée.
  6. En fait, on peut cacher plus d'informations que juste les méthodes, on peut aussi pré-calculer le nom des propriétés pour éviter d'accéder aux annotations à chaque appel.
    Écrire le code qui pré-calcule le maximum de choses pour que l'appel à toJSON soit le plus efficace possible.
    Indication: quelle est la lettre grecque entre kappa et mu?