java to - ¿Cómo puedo convertir JSON a HashMap usando Gson?




fromjson list (14)

Solicito datos de un servidor que devuelve datos en formato JSON. Convertir un HashMap en JSON cuando se realizó la solicitud no fue difícil en absoluto, pero la otra manera parece ser un poco difícil. La respuesta de JSON se ve así:

{ 
    "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 qué manera sería más fácil acceder a estos datos? Estoy usando el módulo GSON.


Answers

Esto es lo que he estado usando:

public static HashMap<String, Object> parse(String json) {
    JsonObject object = (JsonObject) parser.parse(json);
    Set<Map.Entry<String, JsonElement>> set = object.entrySet();
    Iterator<Map.Entry<String, JsonElement>> iterator = set.iterator();
    HashMap<String, Object> map = new HashMap<String, Object>();
    while (iterator.hasNext()) {
        Map.Entry<String, JsonElement> 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;
}

Utilicé este código:

Gson gson = new Gson();
HashMap<String, Object> fields = gson.fromJson(json, HashMap.class);

Aqui tienes:

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);

Sé que esta es una pregunta bastante antigua, pero estaba buscando una solución para deserializar genéricamente JSON anidado a un Map<String, Object> , y no encontré nada.

La forma en que funciona el deserializador de yaml hace que los objetos JSON predeterminados se asignen a Map<String, Object> cuando no se especifica un tipo, pero gson no parece hacer esto. Afortunadamente, puedes lograrlo con un deserializador personalizado.

Utilicé el siguiente deserializador para deserializar de forma natural cualquier cosa, de forma JsonObject s Map<String, Object> y JsonArray s to Object[] s, donde todos los elementos JsonArray están deserializados de manera similar.

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;
  }
}

El desorden dentro del método handlePrimitive es para asegurarse de que solo obtenga un Double o un Integer o un Long, y probablemente podría ser mejor, o al menos simplificado si está de acuerdo con obtener BigDecimals, que creo que es el valor predeterminado.

Puedes registrar este adaptador como:

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

Y luego llámalo como:

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

No estoy seguro de por qué este no es el comportamiento predeterminado en gson, ya que está en la mayoría de las otras bibliotecas de serialización semiestructuradas ...


Puedes usar esta clase en su lugar :) (maneja incluso listas, listas anidadas y json)

public class Utility {

    public static Map<String, Object> 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<String, Object> _jsonToMap_(JSONObject json) throws JSONException {
        Map<String, Object> retMap = new HashMap<String, Object>();

        if(json != JSONObject.NULL) {
            retMap = toMap(json);
        }
        return retMap;
    }


    private static Map<String, Object> toMap(JSONObject object) throws JSONException {
        Map<String, Object> map = new HashMap<String, Object>();

        Iterator<String> 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<Object> toList(JSONArray array) throws JSONException {
        List<Object> list = new ArrayList<Object>();
        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 convertir su cadena JSON a hashmap use esto:

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

He superado un problema similar con un JsonDeSerializer personalizado. Traté de hacerlo un poco genérico, pero aún no es suficiente. Aunque es una solución que se ajusta a mis necesidades.

En primer lugar, necesita implementar un nuevo JsonDeserializer para objetos de mapa.

public class MapDeserializer<T, U> implements JsonDeserializer<Map<T, U>>

Y el método de deserialización se verá similar a esto:

public Map<T, U> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {

        if (!json.isJsonObject()) {
            return null;
        }

        JsonObject jsonObject = json.getAsJsonObject();
        Set<Entry<String, JsonElement>> jsonEntrySet = jsonObject.entrySet();
        Map<T, U> deserializedMap = new HashMap<T, U>();

        for (Entry<java.lang.String, JsonElement> 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;
    }

La contra con esta solución, es que la clave de mi Mapa es siempre de Tipo "Cadena". Sin embargo, al cambiar algunas cosas, alguien puede hacer que sea genérico. Además, debo decir que la clase del valor debe pasarse en el constructor. Por lo tanto, el método getMyType() en mi código devuelve el tipo de los valores del Mapa, que se pasó en el constructor.

Puede consultar esta publicación. ¿Cómo escribo un deserializador JSON personalizado para Gson? con el fin de aprender más sobre deserializadores personalizados.


Aquí hay una sola línea que lo hará:

HashMap<String, Object> myMap =
   gson.fromJson(yourJson, new TypeToken<HashMap<String, Object>>(){}.getType());

Abajo esta soportado desde gson 2.8.0

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

public static  <K,V> HashMap<K,V> fromMap(String json, Class<K> keyType, Class<V> valueType){
    return gson.fromJson(json, getMapType(keyType,valueType));
}

Con Gson 2.7 de Google (probablemente versiones anteriores también, pero probé con la versión actual 2.7) es tan simple como:

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

Lo que devuelve un Map de tipo com.google.gson.internal.LinkedTreeMap y funciona recursivamente en objetos anidados, matrices, etc.

Ejecuté el ejemplo de OP como tal (simplemente reemplazé las comillas dobles con espacios en blanco y eliminé los espacios en blanco):

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);

Y consiguió la siguiente salida:

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}

Este código funciona:

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());

Esto es más una adición a la respuesta de Kevin Dolan que una respuesta completa, pero estaba teniendo problemas para extraer el tipo del Número. Esta es mi solución:

private Object handlePrimitive(JsonPrimitive json) {
  if(json.isBoolean()) {
    return json.getAsBoolean();
  } else if(json.isString())
    return json.getAsString();
  }

  Number num = element.getAsNumber();

  if(num instanceof Integer){
    map.put(fieldName, num.intValue());
  } else if(num instanceof Long){
    map.put(fieldName, num.longValue());
  } else if(num instanceof Float){
    map.put(fieldName, num.floatValue());
  } else {    // Double
     map.put(fieldName, num.doubleValue());
  }
}

Intenta esto, funcionará. Lo usé para Hashtable .

public static Hashtable<Integer, KioskStatusResource> parseModifued(String json) {
    JsonObject object = (JsonObject) new com.google.gson.JsonParser().parse(json);
    Set<Map.Entry<String, JsonElement>> set = object.entrySet();
    Iterator<Map.Entry<String, JsonElement>> iterator = set.iterator();

    Hashtable<Integer, KioskStatusResource> map = new Hashtable<Integer, KioskStatusResource>();

    while (iterator.hasNext()) {
        Map.Entry<String, JsonElement> 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;
}

Reemplace KioskStatusResource a su clase e Integer a su clase clave.


 HashMap<String, String> jsonToMap(String JsonDetectionString) throws JSONException {

    HashMap<String, String> map = new HashMap<String, String>();
    Gson gson = new Gson();

    map = (HashMap<String, String>) gson.fromJson(JsonDetectionString, map.getClass());

    return map;

}

Tuve el mismo problema, y ​​lo resolví así:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

(¡La gente que responde "No es posible ordenar un dict" no leyó la pregunta! De hecho, "Puedo ordenar las claves, pero ¿cómo puedo ordenar según los valores?" Claramente significa que quiere una lista de las claves ordenadas según el valor de sus valores.)

Tenga en cuenta que el pedido no está bien definido (las claves con el mismo valor estarán en un orden arbitrario en la lista de salida).





java json dictionary hashmap gson