it-swarm.com.de

Wie kann ich JSON mit Gson in eine HashMap konvertieren?

Ich fordere Daten von einem Server an, der Daten im JSON-Format zurückgibt. Das Einfügen einer HashMap in JSON bei der Anforderung war überhaupt nicht schwierig, der umgekehrte Weg scheint jedoch etwas schwierig zu sein. Die JSON-Antwort sieht folgendermaßen aus:

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

Wie kann man am einfachsten auf diese Daten zugreifen? Ich verwende das GSON-Modul.

246

Bitte schön:

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

Dieser Code funktioniert:

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

Ich weiß, dass dies eine ziemlich alte Frage ist, aber ich suchte nach einer Lösung, um verschachtelte JSON generisch zu einem Map<String, Object> zu deserialisieren und nichts gefunden.

So wie mein yaml Deserializer funktioniert, werden JSON-Objekte standardmäßig auf Map<String, Object> gesetzt, wenn Sie keinen Typ angeben, aber gson scheint dies nicht zu tun. Glücklicherweise können Sie dies mit einem benutzerdefinierten Deserializer erreichen.

Ich habe den folgenden Deserializer verwendet, um auf natürliche Weise etwas zu deserialisieren, wobei JsonObjects auf Map<String, Object> und JsonArrays auf Object[]s gesetzt wurde, wobei alle Kinder gleichermaßen deserialisiert werden.

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

Die Unordnung in der handlePrimitive-Methode sorgt dafür, dass Sie immer nur ein Double, eine Integer oder eine Long erhalten, und es könnte wahrscheinlich besser sein oder zumindest vereinfacht werden, wenn Sie mit BigDecimals zufrieden sind, was meiner Meinung nach die Standardeinstellung ist.

Sie können diesen Adapter wie folgt registrieren:

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

Und dann nennen Sie es wie:

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

Ich bin nicht sicher, warum dies nicht das Standardverhalten in Gson ist, da es in den meisten anderen semi-strukturierten Serialisierungsbibliotheken enthalten ist ...

75
Kevin Dolan

Mit Googles Gson 2.7 (wahrscheinlich auch früheren Versionen, aber ich habe es mit der aktuellen Version 2.7 getestet) ist es so einfach wie:

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

Gibt eine Map vom Typ com.google.gson.internal.LinkedTreeMap zurück und funktioniert rekursiv bei verschachtelten Objekten, Arrays usw.

Ich habe das OP-Beispiel so ausgeführt (einfach doppelte durch einfache Anführungszeichen ersetzt und Leerzeichen entfernt):

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

Und bekam folgende Ausgabe:

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}
24
isapir

Update für neue Gson lib:
Sie können jetzt geschachtelte Json-Dateien direkt in Map parsen, sollten sich jedoch dessen bewusst sein, falls Sie versuchen, Json-Typen in Map<String, Object>-Typen zu parsen: Dies führt zu einer Ausnahme. Um dies zu beheben, deklarieren Sie das Ergebnis einfach als LinkedTreeMap-Typ. Beispiel unten:

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

Ich hatte genau die gleiche Frage und bin hier gelandet. Ich hatte einen anderen Ansatz, der viel einfacher erscheint (vielleicht neuere Versionen von Gson?).

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

mit dem folgenden json 

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

Folgende

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

ausgänge

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

Sie können die Verwendung von instanceof beim Navigieren in Ihrem jsonObject dynamisch überprüfen. So etwas wie

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");
} ...

Es funktioniert für mich, also muss es für Sie funktionieren ;-)

11
krico

Versuchen Sie dies, es wird funktionieren. Ich habe es fürHashtableverwendet.

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

Ersetzen SieKioskStatusResourcein Ihre Klasse undIntegerin Ihre Schlüsselklasse.

3
R4U

Nachfolgend wird seit dem Gson 2.8.0 unterstützt

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));
}
3
Leon

Folgendes habe ich verwendet:

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

Sie können diese Klasse stattdessen verwenden :) (behandelt auch Listen, verschachtelte Listen und 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;
    }
}

Um Ihre JSON-Zeichenfolge in Hashmap umzuwandeln, verwenden Sie Folgendes: 

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

Ich habe ein ähnliches Problem mit einem benutzerdefinierten JsonDeSerializer gelöst. Ich habe versucht, es etwas generisch zu machen, aber immer noch nicht genug. Es ist jedoch eine Lösung, die meinen Bedürfnissen entspricht. 

Zunächst müssen Sie einen neuen JsonDeserializer für Map-Objekte implementieren.

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

Die Deserialisierungsmethode sieht etwa so aus:

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

Der Nachteil dieser Lösung ist, dass der Schlüssel meiner Map immer vom Typ "String" ist. Durch das Ändern einiger Dinge kann jemand es jedoch generisch machen. Darüber hinaus muss ich sagen, dass die Klasse des Werts im Konstruktor übergeben werden sollte. Die Methode getMyType() in meinem Code gibt also den Typ der Map-Werte zurück, die im Konstruktor übergeben wurden.

Sie können auf diesen Beitrag verweisen Wie schreibe ich einen benutzerdefinierten JSON-Deserializer für Gson? um mehr über benutzerdefinierte Deserialisierer zu erfahren.

1
nikkatsa

Hier ist ein Einzeiler, der es tun wird:

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

Dies ist mehr ein Nachtrag zu Kevin Dolans Antwort als eine vollständige Antwort, aber ich hatte Probleme, den Typ aus der Zahl zu extrahieren. Das ist meine Lösung:

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());
  }
}
0
Luke Salamone