Convertir JSON en carte avec Gson

Convertir JSON en carte avec Gson

1. introduction

In this quick tutorial, we’ll learn how to convert a JSON string to a Map using Gson from Google.

Nous verrons trois approches différentes pour y parvenir et discuterons de leurs avantages et inconvénients - avec quelques exemples pratiques.

2. RéussiteMap.class

En général,Gson provides the following API in its Gson class to convert a JSON string to an object:

public  T fromJson(String json, Class classOfT) throws JsonSyntaxException;

D'après la signature, il est très clair que le deuxième paramètre est la classe de l'objet que nous voulons analyser par le JSON. Dans notre cas, ce devrait êtreMap.class:

String jsonString = "{'employee.name':'Bob','employee.salary':10000}";
Gson gson = new Gson();
Map map = gson.fromJson(jsonString, Map.class);
Assert.assertEquals(2, map.size());
Assert.assertEquals(Double.class, map.get("employee.salary").getClass());

Cette approche déterminera au mieux le type de valeur de chaque propriété.

Par exemple, les nombres seront forcés enDoubles,true ablefalse enBoolean et les objets enLinkedTreeMaps.

S'il y a des clés en double, cependant, la coercition échouera et elle lancera unJsonSyntaxException.

Et, en raison detype erasure,, nous ne pourrons pas non plus configurer ce comportement de coercition. Ainsi, si nous devons spécifier les types de clé ou de valeur, nous aurons besoin d'une approche différente.

3. Utilisation deTypeToken

To overcome the problem of type-erasure for the generic types, Gson has an overloaded version of the API:

public  T fromJson(String json, Type typeOfT) throws JsonSyntaxException;

Nous pouvons construire unMap avec ses paramètres de type en utilisant lesTypeToken de Gson. The TypeToken class returns an instance of ParameterizedTypeImpl that preserves the type of the key and value even at runtime:

String jsonString = "{'Bob' : {'name': 'Bob Willis'},"
  + "'Jenny' : {'name': 'Jenny McCarthy'}, "
  + "'Steve' : {'name': 'Steven Waugh'}}";
Gson gson = new Gson();
Type empMapType = new TypeToken>() {}.getType();
Map nameEmployeeMap = gson.fromJson(jsonString, empMapType);
Assert.assertEquals(3, nameEmployeeMap.size());
Assert.assertEquals(Employee.class, nameEmployeeMap.get("Bob").getClass());

Maintenant, si nous construisons notre typeMap commeMap<String, Object>, alors l'analyseur sera toujours par défaut comme nous l'avons vu dans la section précédente.

Bien sûr, cela revient toujours à Gson pour avoir forcé les types primitifs. Celles-ci peuvent toutefois être personnalisées aussi.

4. Utilisation deJsonDeserializer personnalisés

Lorsque nous avons besoin d'un contrôle fin sur la construction de notre objetMap, nous pouvons implémenter un désérialiseur personnalisé de typeJsonDeserializer<Map>.

Pour voir un exemple, supposons que notre JSON contienne le nom de l'employé comme clé et sa date d'embauche comme valeur. De plus, supposons que le format de la date estyyyy/MM/dd, qui n’est pas un format standard pourGson.

Nous pouvons configurer Gson pour analyser notre carte différemment, puis, en implémentant unJsonDeserializer:

public class StringDateMapDeserializer implements JsonDeserializer> {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");

    @Override
    public Map deserialize(JsonElement elem,
          Type type,
          JsonDeserializationContext jsonDeserializationContext) {
        return elem.getAsJsonObject()
          .entrySet()
          .stream()
          .filter(e -> e.getValue().isJsonPrimitive())
          .filter(e -> e.getValue().getAsJsonPrimitive().isString())
          .collect(
            Collectors.toMap(
              Map.Entry::getKey,
              e -> formatDate(e.getValue())));
    }

    private Date formatDate(Object value) {
        try {
            return format(value.getAsString());
        } catch (ParseException ex) {
            throw new JsonParseException(ex);
        }
    }
}

Maintenant, nous devons l'enregistrer dans leGsonBuilder par rapport à notre type de cibleMap<String, Date> et construire un objetGson personnalisé.

Lorsque nous appelons l'APIfromJson sur cet objetGson, l'analyseur appelle le désérialiseur personnalisé et renvoie l'instanceMap souhaitée:

String jsonString = "{'Bob': '2017-06-01', 'Jennie':'2015-01-03'}";
Type type = new TypeToken>(){}.getType();
Gson gson = new GsonBuilder()
  .registerTypeAdapter(type, new StringDateMapDeserializer())
  .create();
Map empJoiningDateMap = gson.fromJson(jsonString, type);
Assert.assertEquals(2, empJoiningDateMap.size());
Assert.assertEquals(Date.class, empJoiningDateMap.get("Bob").getClass());

Cette tactique est également utile lorsque notre carte peut contenir des valeurs hétérogènes et nous avons une idée juste du nombre de types de valeurs pouvant exister.

Pour en savoir plus sur un désérialiseur personnalisé dansGson, n'hésitez pas à parcourir lesGson Deserialization Cookbook.

5. Conclusion

Dans ce court article, nous avons appris plusieurs manières de construire une carte à partir d'une chaîne au format JSON. Et nous avons également discuté des cas d'utilisation appropriés pour ces variations.

Le code source des exemples est disponibleover on GitHub.