Como posso converter JSON para um HashMap usando o Gson?

Estou solicitando dados de um servidor que retorna dados no formato JSON. Lançar um HashMap em JSON ao fazer o pedido não foi difícil, mas o outro caminho parece ser um pouco complicado. A resposta JSON é assim:

{ "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" } 

De que maneira seria mais fácil acessar esses dados? Estou usando o módulo GSON.

Aqui está:

 import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken; Type type = new TypeToken>(){}.getType(); Map myMap = gson.fromJson("{'k1':'apple','k2':'orange'}", type); 

Este código funciona:

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

Eu sei que esta é uma questão bastante antiga, mas eu estava procurando uma solução para desserializar genericamente JSON nested para um Map e não encontrei nada.

A maneira como meu deserializador yaml funciona, ele padroniza objects JSON para Map quando você não especifica um tipo, mas o gson não parece fazer isso. Felizmente você pode fazer isso com um desserializador personalizado.

Eu usei o seguinte desserializador para desserializar naturalmente qualquer coisa, padronizando JsonObject s para Map e JsonArray s para Object[] s, onde todos os filhos são desserializados da mesma forma.

 private static class NaturalDeserializer implements JsonDeserializer { 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 map = new HashMap(); for(Map.Entry entry : json.entrySet()) map.put(entry.getKey(), context.deserialize(entry.getValue(), Object.class)); return map; } } 

A bagunça dentro do método handlePrimitive é para garantir que você só receba um Double, um Integer ou um Long, e provavelmente poderia ser melhor, ou pelo menos simplificado, se você estiver certo em obter BigDecimals, que acredito ser o padrão.

Você pode registrar este adaptador como:

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

E então chame como:

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

Eu não tenho certeza porque este não é o comportamento padrão no gson, já que é na maioria das outras bibliotecas de serialização semiestruturada …

Atualização para a nova biblioteca do Gson:
Agora você pode analisar o Json nested no mapa diretamente, mas você deve estar ciente caso tente analisar o Json para Map type: ele aumentará a exceção. Para corrigir isso, apenas declare o resultado como tipo LinkedTreeMap . Exemplo abaixo:

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

Com o Gson 2.7 do google (provavelmente versões anteriores também, mas testei com a versão atual 2.7) é tão simples quanto:

 Map map = gson.fromJson(jsonString, Map.class); 

Que retorna um Map do tipo com.google.gson.internal.LinkedTreeMap e funciona recursivamente em objects nesteds, matrizes etc.

Eu executei o exemplo do OP assim (simplesmente substitui o dobro – com aspas simples e espaços em branco removidos):

 String jsonString = "{'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'}"; Map map = gson.fromJson(jsonString, Map.class); System.out.println(map.getClass().toString()); System.out.println(map); 

E obteve a seguinte saída:

 class com.google.gson.internal.LinkedTreeMap {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} 

Eu tive exatamente a mesma pergunta e acabei aqui. Eu tive uma abordagem diferente que parece muito mais simples (talvez versões mais recentes do gson?).

  Gson gson = new Gson(); Map jsonObject = (Map) gson.fromJson(data, Object.class); 

com o seguinte json

 { "map-00": { "array-00": [ "entry-00", "entry-01" ], "value": "entry-02" } } 

Os seguintes

  Map map00 = (Map) jsonObject.get("map-00"); List array00 = (List) map00.get("array-00"); String value = (String) map00.get("value"); for (int i = 0; i < array00.size(); i++) { System.out.println("map-00.array-00[" + i + "]= " + array00.get(i)); } System.out.println("map-00.value = " + value); 

saídas

 map-00.array-00[0]= entry-00 map-00.array-00[1]= entry-01 map-00.value = entry-02 

Você pode verificar dinamicamente usando instanceof ao navegar pelo seu jsonObject. Algo como

 Map json = gson.fromJson(data, Object.class); if(json.get("field") instanceof Map) { Map field = (Map)json.get("field"); } else if (json.get("field") instanceof List) { List field = (List)json.get("field"); } ... 

Funciona para mim, então deve funcionar para você 😉

Tente isso, vai funcionar. Eu usei para Hashtable .

 public static Hashtable parseModifued(String json) { JsonObject object = (JsonObject) new com.google.gson.JsonParser().parse(json); Set> set = object.entrySet(); Iterator> iterator = set.iterator(); Hashtable map = new Hashtable(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); Integer key = Integer.parseInt(entry.getKey()); KioskStatusResource value = new Gson().fromJson(entry.getValue(), KioskStatusResource.class); if (value != null) { map.put(key, value); } } return map; } 

Substitua KioskStatusResource pela sua class e Integer pela sua class-chave.

Aqui está o que eu tenho usado:

 public static HashMap parse(String json) { JsonObject object = (JsonObject) parser.parse(json); Set> set = object.entrySet(); Iterator> iterator = set.iterator(); HashMap map = new HashMap(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); String key = entry.getKey(); JsonElement value = entry.getValue(); if (!value.isJsonPrimitive()) { map.put(key, parse(value.toString())); } else { map.put(key, value.getAsString()); } } return map; } 

Abaixo é suportado desde gson 2.8.0

 public static Type getMapType(Class keyType, Class valueType){ return TypeToken.getParameterized(HashMap.class, keyType, valueType).getType(); } public static  HashMap fromMap(String json, Class keyType, Class valueType){ return gson.fromJson(json, getMapType(keyType,valueType)); } 

Eu superei um problema semelhante com um Custom JsonDeSerializer. Eu tentei torná-lo um pouco genérico, mas ainda não é suficiente. É uma solução que atende às minhas necessidades.

Primeiro de tudo você precisa implementar um novo JsonDeserializer para objects Map.

 public class MapDeserializer implements JsonDeserializer> 

E o método de desserialização será semelhante a este:

 public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (!json.isJsonObject()) { return null; } JsonObject jsonObject = json.getAsJsonObject(); Set> jsonEntrySet = jsonObject.entrySet(); Map deserializedMap = new HashMap(); for (Entry entry : jsonEntrySet) { try { U value = context.deserialize(entry.getValue(), getMyType()); deserializedMap.put((T) entry.getKey(), value); } catch (Exception ex) { logger.info("Could not deserialize map.", ex); } } return deserializedMap; } 

O con com esta solução, é que a chave do meu mapa é sempre do tipo “String”. No entanto, ao chamar algumas coisas, alguém pode torná-lo genérico. Além disso, eu preciso dizer que a class do valor deve ser passada no construtor. Portanto, o método getMyType() no meu código retorna o tipo de valores do Map, que foi passado no construtor.

Você pode fazer referência a este post Como escrevo um desserializador JSON customizado para o Gson? a fim de aprender mais sobre os deserializadores personalizados.

Você pode usar essa class 🙂 (lida com listas pares, listas aninhadas e json)

 public class Utility { public static Map jsonToMap(Object json) throws JSONException { if(json instanceof JSONObject) return _jsonToMap_((JSONObject)json) ; else if (json instanceof String) { JSONObject jsonObject = new JSONObject((String)json) ; return _jsonToMap_(jsonObject) ; } return null ; } private static Map _jsonToMap_(JSONObject json) throws JSONException { Map retMap = new HashMap(); if(json != JSONObject.NULL) { retMap = toMap(json); } return retMap; } private static Map toMap(JSONObject object) throws JSONException { Map map = new HashMap(); Iterator keysItr = object.keys(); while(keysItr.hasNext()) { String key = keysItr.next(); Object value = object.get(key); if(value instanceof JSONArray) { value = toList((JSONArray) value); } else if(value instanceof JSONObject) { value = toMap((JSONObject) value); } map.put(key, value); } return map; } public static List toList(JSONArray array) throws JSONException { List list = new ArrayList(); for(int i = 0; i < array.length(); i++) { Object value = array.get(i); if(value instanceof JSONArray) { value = toList((JSONArray) value); } else if(value instanceof JSONObject) { value = toMap((JSONObject) value); } list.add(value); } return list; } } 

Para converter sua string JSON em hashmap, use:

 HashMap hashMap = new HashMap<>(Utility.jsonToMap(response)) ; 

Aqui está um one-liner que fará:

 HashMap myMap = gson.fromJson(yourJson, new TypeToken>(){}.getType()); 
  HashMap jsonToMap(String JsonDetectionString) throws JSONException { HashMap map = new HashMap(); Gson gson = new Gson(); map = (HashMap) gson.fromJson(JsonDetectionString, map.getClass()); return map; } 

O JSONObject normalmente usa o HashMap internamente para armazenar os dados. Então, você pode usá-lo como Map no seu código.

Exemplo,

 JSONObject obj = JSONObject.fromObject(strRepresentation); Iterator i = obj.entrySet().iterator(); while (i.hasNext()) { Map.Entry e = (Map.Entry)i.next(); System.out.println("Key: " + e.getKey()); System.out.println("Value: " + e.getValue()); } 

Eu usei este código:

 Gson gson = new Gson(); HashMap fields = gson.fromJson(json, HashMap.class);