310 votes

Comment convertir JSON en HashMap en utilisant Gson ?

Je demande des données à un serveur qui les renvoie au format JSON. La transformation d'un HashMap en JSON lors de la requête n'a pas été difficile, mais l'inverse semble être un peu plus délicat. La réponse JSON ressemble à ceci :

{ 
    "header" : { 
        "alerts" : [ 
            {
                "AlertID" : "2",
                "TSExpires" : null,
                "Target" : "1",
                "Text" : "woot",
                "Type" : "1"
            },
            { 
                "AlertID" : "3",
                "TSExpires" : null,
                "Target" : "1",
                "Text" : "woot",
                "Type" : "1"
            }
        ],
        "session" : "0bc8d0835f93ac3ebbf11560b2c5be9a"
    },
    "result" : "4be26bc400d3c"
}

Quel serait le moyen le plus simple d'accéder à ces données ? J'utilise le module GSON.

28 votes

Map<String,Object> result = new Gson().fromJson(json, Map.class); fonctionne avec gson 2.6.2.

1 votes

Comment se reconvertir ? Je veux dire de Map à Json Array.

510voto

Tito Cheriachan Points 1432

Voilà :

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

Type type = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> myMap = gson.fromJson("{'k1':'apple','k2':'orange'}", type);

6 votes

C'est une bonne chose mais je n'aime pas l'utiliser TypeToken - il fait un casting implicite à l'intérieur.

1 votes

Casting to Map<>, vous avez mis fin à mes heures de frustration !

1 votes

Est-ce un json valide dans l'exemple ?

121voto

Angel Points 463

Ce code fonctionne :

Gson gson = new Gson(); 
String json = "{\"k1\":\"v1\",\"k2\":\"v2\"}";
Map<String,Object> map = new HashMap<String,Object>();
map = (Map<String,Object>) gson.fromJson(json, map.getClass());

3 votes

Cela convertira les ints en floats avant de les transformer en chaînes de caractères, mais cela fonctionnera pour convertir JSON en cartes à des fins de comparaison.

13 votes

Fonctionne très bien pour moi, mais j'ai changé la carte en Map<String, Object> parce que si le json n'est pas uniquement composé de chaînes de caractères, vous obtenez une erreur

6 votes

Cela donne une mauvaise impression. La solution correcte pour les types paramétrés est TypeToken .

78voto

Kevin Dolan Points 1802

Je sais qu'il s'agit d'une question assez ancienne, mais je cherchais une solution pour désérialiser de manière générique des JSON imbriqués dans un fichier de type Map<String, Object> et n'a rien trouvé.

La façon dont mon désérialiseur yaml fonctionne, il donne par défaut aux objets JSON la valeur suivante Map<String, Object> lorsque vous ne spécifiez pas de type, mais gson ne semble pas le faire. Heureusement, vous pouvez le faire avec un désérialiseur personnalisé.

J'ai utilisé le désérialiseur suivant pour désérialiser naturellement n'importe quoi, par défaut JsonObject s à Map<String, Object> y JsonArray s à Object[] s, où tous les enfants sont désérialisés de la même manière.

private static class NaturalDeserializer implements JsonDeserializer<Object> {
  public Object deserialize(JsonElement json, Type typeOfT, 
      JsonDeserializationContext context) {
    if(json.isJsonNull()) return null;
    else if(json.isJsonPrimitive()) return handlePrimitive(json.getAsJsonPrimitive());
    else if(json.isJsonArray()) return handleArray(json.getAsJsonArray(), context);
    else return handleObject(json.getAsJsonObject(), context);
  }
  private Object handlePrimitive(JsonPrimitive json) {
    if(json.isBoolean())
      return json.getAsBoolean();
    else if(json.isString())
      return json.getAsString();
    else {
      BigDecimal bigDec = json.getAsBigDecimal();
      // Find out if it is an int type
      try {
        bigDec.toBigIntegerExact();
        try { return bigDec.intValueExact(); }
        catch(ArithmeticException e) {}
        return bigDec.longValue();
      } catch(ArithmeticException e) {}
      // Just return it as a double
      return bigDec.doubleValue();
    }
  }
  private Object handleArray(JsonArray json, JsonDeserializationContext context) {
    Object[] array = new Object[json.size()];
    for(int i = 0; i < array.length; i++)
      array[i] = context.deserialize(json.get(i), Object.class);
    return array;
  }
  private Object handleObject(JsonObject json, JsonDeserializationContext context) {
    Map<String, Object> map = new HashMap<String, Object>();
    for(Map.Entry<String, JsonElement> entry : json.entrySet())
      map.put(entry.getKey(), context.deserialize(entry.getValue(), Object.class));
    return map;
  }
}

Le désordre à l'intérieur de la handlePrimitive permet de s'assurer que vous n'obtenez jamais qu'un Double, un Integer ou un Long, et pourrait probablement être améliorée, ou du moins simplifiée si vous êtes d'accord pour obtenir des BigDecimals, ce qui est, je crois, la valeur par défaut.

Vous pouvez enregistrer cet adaptateur comme :

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Object.class, new NaturalDeserializer());
Gson gson = gsonBuilder.create();

Et ensuite l'appeler comme ça :

Object natural = gson.fromJson(source, Object.class);

Je ne sais pas pourquoi ce n'est pas le comportement par défaut dans gson, puisque c'est le cas dans la plupart des autres bibliothèques de sérialisation semi-structurée...

1 votes

... bien que je ne sache pas trop quoi faire maintenant avec les objets que je récupère. Je n'arrive pas à les convertir en String, même si je sais que ce sont des strings.

1 votes

Aha ! L'astuce était d'appeler le désérialiseur récursivement au lieu de l'appel context.deserialize().

0 votes

Merci pour cela. C'est un peu étrange que cela ne se fasse pas tout seul.

42voto

BalusC Points 498232

Utilisez cette structure de données :

public class Data {
    private Header header;
    private String result;
    // Add/generate getters and setters.

    public static class Header {
        private List<Alert> alerts;
        private String session;
        // Add/generate getters and setters.
    }

    public static class Alert {
        private Long AlertID;
        private Object TSExpires;
        private Integer Target;
        private String Text;
        private Integer Type;
        // Add/generate getters and setters. 
        // PS: I would lowercase the property names in both JSON as this class.
    }
}

Et utilise cet oneliner pour le reconvertir :

Data data = new Gson().fromJson(json, Data.class);

32voto

Hoang Huu Points 141

Mise à jour pour la nouvelle librairie Gson :
Vous pouvez maintenant analyser directement les Json imbriqués en Map, mais vous devez faire attention si vous essayez d'analyser les Json en Map. Map<String, Object> type : une exception sera levée. Pour résoudre ce problème, il suffit de déclarer le résultat comme LinkedTreeMap type. Exemple ci-dessous :

String nestedJSON = "{\"id\":\"1\",\"message\":\"web_didload\",\"content\":{\"success\":1}}";
Gson gson = new Gson();
LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);

0 votes

D'où puis-je importer LinkedTreeMap ? Je ne le trouve pas dans le code Gson.

0 votes

Si je me souviens bien, LinkedTreeMap est défini dans la nouvelle librairie Gson. Vous pouvez vérifier ici : code.google.com/p/google-gson/source/browse/trunk/gson/src/main/

1 votes

Pour moi, cela fonctionne aussi avec Map<String,Object> result = gson.fromJson(json , Map.class); . Utilisation de gson 2.6.2.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X