Wie kann ich JSON mit Gson in eine HashMap konvertieren?

286

Ich fordere Daten von einem Server an, der Daten im JSON-Format zurückgibt. Das Casting einer HashMap in JSON beim Erstellen der Anfrage war überhaupt nicht schwierig, aber der andere Weg scheint 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 benutze das GSON-Modul.

Mridang Agarwalla
quelle
23
Map<String,Object> result = new Gson().fromJson(json, Map.class);funktioniert mit gson 2.6.2.
Ferran Maylinch
1
Wie konvertiere ich zurück? Ich meine von Map zu Json Array.
K.Sopheak

Antworten:

471

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);
Cherit
quelle
6
Gut, aber ich benutze TypeTokenes nicht gerne - es impliziert implizites Casting im Inneren.
AlikElzin-Kilaka
Casting to Map <>, du hast meine Stunden der Frustration beendet!
Dexter
1
Ist das im Beispiel ein gültiger JSON?
Evan Kairuz
@EvanKairuz Nein, das ist es nicht. Es sollte sein{"k1":"apple","k2":"orange"}
Vadim Kotov
Was ist, wenn ich einen String konvertieren muss, der eigentlich ein Array ist
Ravi Yadav
111

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());
Engel
quelle
1
Dadurch werden Ints in Floats konvertiert, bevor sie in Strings umgewandelt werden. JSON wird jedoch zu Vergleichszwecken in Maps konvertiert.
Louielouie
12
funktioniert gut für mich, aber ich habe die Karte geändert, Map<String, Object>denn wenn der JSON nicht nur Zeichenfolgen ist, erhalten Sie eine Fehlermeldung
Moshe Shaham
5
Dies macht den falschen Eindruck. Die richtige Lösung für parametrisierte Typen ist TypeToken.
Sotirios Delimanolis
Dies wäre eine generische Lösung für alle Typen, aber etwas ungewöhnlich.
Leon
76

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

So wie mein yaml-Deserializer funktioniert, werden JSON-Objekte standardmäßig verwendet, Map<String, Object>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 alles auf natürliche Weise zu deserialisieren, wobei standardmäßig JsonObjects bis s Map<String, Object>und JsonArrays bis Object[]s verwendet werden, wobei alle Kinder auf ähnliche Weise 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 handlePrimitiveMethode besteht darin, sicherzustellen, dass Sie immer nur ein Double oder ein Integer oder ein Long erhalten und wahrscheinlich besser oder zumindest vereinfacht sein könnten, wenn Sie mit BigDecimals einverstanden 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 nenne es wie:

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

Ich bin mir nicht sicher, warum dies nicht das Standardverhalten in gson ist, da es in den meisten anderen halbstrukturierten Serialisierungsbibliotheken vorkommt ...

Kevin Dolan
quelle
1
... obwohl ich nicht ganz sicher bin, was ich jetzt mit den Objekten machen soll, die ich zurück bekomme. Ich kann sie nicht als String besetzen, obwohl ich weiß, dass sie Strings sind
Matt Zukowski
1
Aha! Der Trick war der rekursive Aufruf des Deserializers anstelle des Aufrufs context.deserialize ().
Matt Zukowski
1
Hättest du Code Matt? Ich versuche, die Änderungen am Deserializer vorzunehmen, aber ich kann Ihren Standpunkt nicht wirklich erkennen
Romain Piel
5
Gson scheint nun standardmäßig das Verhalten zu haben, das Kevin Dolan in seinem Code-Snippet anstrebt.
eleotlecram
1
@SomeoneSomewhere siehe akzeptierte Antwort hier stackoverflow.com/questions/14944419/gson-to-hashmap
MY
32

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

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

Dies gibt einen MapTyp zurück com.google.gson.internal.LinkedTreeMapund funktioniert rekursiv für verschachtelte Objekte, Arrays usw.

Ich habe das OP-Beispiel so ausgeführt (einfach doppelt 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 die 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}
Isapir
quelle
25

Update für neue Gson-Bibliothek:
Sie können verschachtelte Json jetzt direkt in Map analysieren. Sie sollten sich jedoch bewusst sein, falls Sie versuchen, Json nach Map<String, Object>Typ zu analysieren : Es wird eine Ausnahme ausgelöst . Um dies zu beheben, deklarieren Sie einfach das Ergebnis als LinkedTreeMapTyp. Beispiel unten:

String nestedJSON = "{"id":"1","message":"web_didload","content":{"success":1}};
Gson gson = new Gson();
LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);
Hoang Nguyen Huu
quelle
Woher importiere ich LinkedTreeMap? Ich kann es nicht im Gson-Code finden.
HelpMeStackOverflowMyOnlyHope
Soweit ich mich erinnere, ist LinkedTreeMap in der neuen Gson-Bibliothek definiert. Sie können hier überprüfen: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
Hoang Nguyen Huu
1
Bei mir funktioniert es auch mit Map<String,Object> result = gson.fromJson(json , Map.class);. Verwenden von gson 2.6.2.
Ferran Maylinch
12

Ich hatte genau die gleiche Frage und bin hier gelandet. Ich hatte einen anderen Ansatz, der viel einfacher zu sein scheint (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 beim Navigieren in Ihrem jsonObject mithilfe von instanceof dynamisch überprüfen. 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 dich funktionieren ;-)

krico
quelle
6

Unten wird seit 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));
}
Leon
quelle
3

Versuchen Sie dies, es wird funktionieren. Ich habe es für Hashtable verwendet .

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 Sie KioskStatusResource durch Ihre Klasse und Integer durch Ihre Schlüsselklasse.

R4U
quelle
Dies funktionierte für mich, nachdem HashMap eine LinkedTreeMap-Ausnahme ausgelöst hatte.
Newfivefour
2

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

Hier ist ein Einzeiler, der es schafft:

HashMap<String, Object> myMap =
   gson.fromJson(yourJson, new TypeToken<HashMap<String, Object>>(){}.getType());
Ab ِ
quelle
Ja, es ist eine Zeile, aber denken Sie daran, dass new TypeToken<HashMap<String, Object>>(){}eine neue Inline-Unterklasse erstellt wird und alle Linters zumindest eine Warnung auslösen werden
Martin Meeser,
1

Ich habe ein ähnliches Problem mit einem benutzerdefinierten JsonDeSerializer behoben. Ich habe versucht, es ein bisschen 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 Kartenobjekte implementieren.

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

Und die Deserialisierungsmethode sieht ungefähr 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 Karte immer vom Typ "String" ist. Wenn man jedoch einige Dinge ändert, kann jemand sie generisch machen. Außerdem muss ich sagen, dass die Klasse des Wertes im Konstruktor übergeben werden sollte. Die Methode getMyType()in meinem Code gibt also den Typ der Map-Werte zurück, der im Konstruktor übergeben wurde.

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

Nikkatsa
quelle
1

Sie können diese Klasse stattdessen verwenden :) (behandelt gerade 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;
    }
}

Verwenden Sie Folgendes, um Ihre JSON-Zeichenfolge in eine Hashmap zu konvertieren :

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

Dies ist eher 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());
  }
}
Luke
quelle
-1
 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;

}
user2918406
quelle
-3

JSONObject wird normalerweise HashMapintern zum Speichern der Daten verwendet. Sie können es also als Karte in Ihrem Code verwenden.

Beispiel,

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());
}
Phanindra
quelle
12
Dies ist von json-lib, nicht von gson!
Ammar
-3

Ich habe diesen Code verwendet:

Gson gson = new Gson();
HashMap<String, Object> fields = gson.fromJson(json, HashMap.class);
mortalis
quelle
Dies gibt mir eine ungeprüfte Konvertierungswarnung.
Linie