Jak przekonwertować JSON na HashMap przy użyciu Gson?

286

Proszę o dane z serwera, który zwraca dane w formacie JSON. Rzutowanie HashMap na JSON podczas wysyłania żądania wcale nie było trudne, ale w drugą stronę wydaje się trochę trudne. Odpowiedź JSON wygląda następująco:

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

W jaki sposób najłatwiej byłoby uzyskać dostęp do tych danych? Używam modułu GSON.

Mridang Agarwalla
źródło
23
Map<String,Object> result = new Gson().fromJson(json, Map.class);współpracuje z gson 2.6.2.
Ferran Maylinch
1
jak przekonwertować z powrotem? Mam na myśli od Map do Json Array.
K.Sopheak

Odpowiedzi:

471

Proszę bardzo:

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
źródło
6
Dobry, ale nie lubię go używać TypeToken- to niejawne rzucanie w środku.
AlikElzin-kilaka
Przesyłając na mapę <>, zakończyłeś moje godziny frustracji!
Dexter
1
Czy to prawidłowy Json w przykładzie?
Evan Kairuz
@EvanKairuz Nie, nie jest. Powinno być{"k1":"apple","k2":"orange"}
Vadim Kotov
co jeśli muszę przekonwertować ciąg znaków, który w rzeczywistości jest tablicą
Ravi Yadav
111

Ten kod działa:

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());
Anioł
źródło
1
Spowoduje to konwersję ints na zmiennoprzecinkowe, zanim przekształci je w łańcuchy, ale będzie działało, aby przekonwertować JSON na mapy w celach porównawczych.
louielouie,
12
działa świetnie dla mnie, ale zmieniłem mapę na, Map<String, Object>ponieważ jeśli Json to nie tylko łańcuchy, pojawia się błąd
Moshe Shaham
5
Daje to złe wrażenie. Prawidłowe rozwiązanie dla sparametryzowanych typów to TypeToken.
Sotirios Delimanolis
To byłoby ogólne rozwiązanie dla wszystkich typów, ale trochę rzadkie.
Leon,
76

Wiem, że jest to dość stare pytanie, ale szukałem rozwiązania, aby ogólnie dokonać deserializacji zagnieżdżonego JSON do Map<String, Object> i nic nie znalazłem.

Sposób, w jaki działa mój deserializator yaml, domyślnie ustawia obiekty JSON na Map<String, Object> gdy nie określisz typu, ale wydaje się, że gson tego nie robi. Na szczęście możesz to zrobić za pomocą niestandardowego deserializatora.

Użyłem następujący Deserializator naturalnie deserializowania niczego, zalegających JsonObjectS Map<String, Object>i JsonArrayS, Object[]S, gdzie wszystkie dzieci są podobnie deserializacji.

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

Bałagan w środku handlePrimitive metodzie ma na celu upewnienie się, że zawsze otrzymujesz tylko Double, Integer lub Long, i prawdopodobnie może być lepszy, lub przynajmniej uproszczony, jeśli nie masz nic przeciwko otrzymywaniu BigDecimals, co moim zdaniem jest domyślne.

Możesz zarejestrować ten adapter jak:

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

A potem nazwij to tak:

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

Nie jestem pewien, dlaczego nie jest to domyślne zachowanie w gson, ponieważ jest ono w większości innych częściowo ustrukturyzowanych bibliotek serializacji ...

Kevin Dolan
źródło
1
... chociaż nie jestem pewien, co teraz zrobić z przedmiotami, które otrzymam. Nie mogę rzucić ich na String, choć wiem, że to struny
Matt Zukowski,
1
Aha! Sztuką było wywołanie rekursywnie deserializera zamiast wywołania context.deserialize ().
Matt Zukowski,
1
Czy masz jakiś kod Matt? Próbuję wprowadzić zmiany w deserializatorze, ale tak naprawdę nie rozumiem, o co ci chodzi
Romain Piel
5
Wygląda na to, że Gson teraz zachowuje się tak, jak chce Kevin Dolan w swoim fragmencie kodu.
eleotlecram
1
@SomeoneSomewhere patrz zaakceptowana odpowiedź tutaj stackoverflow.com/questions/14944419/gson-to-hashmap
MY
32

Z Google Gson 2.7 (prawdopodobnie również wcześniejszymi wersjami, ale testowałem z bieżącą wersją 2.7) jest tak prosty, jak:

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

Który zwraca Maptypcom.google.gson.internal.LinkedTreeMap i działa rekurencyjnie na zagnieżdżone obiekty, tablice itp.

Uruchomiłem przykład OP (tak po prostu zastąpiłem double- pojedynczymi cudzysłowami i usunąłem białe znaki):

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

I otrzymałem następujące dane wyjściowe:

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
źródło
25

Aktualizacja dla nowej biblioteki Gson:
Możesz teraz parsować zagnieżdżonego Jsona do mapy, ale powinieneś być ostrożny na wypadek, gdybyś spróbował parsować Jsona, aby Map<String, Object>wpisać: spowoduje to wyjątek. Aby to naprawić, po prostu zadeklaruj wynik jako LinkedTreeMaptyp. Przykład poniżej:

String nestedJSON = "{"id":"1","message":"web_didload","content":{"success":1}};
Gson gson = new Gson();
LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);
Hoang Nguyen Huu
źródło
Skąd mogę importować LinkedTreeMap? Nie mogę tego znaleźć w kodzie Gson.
HelpMeStackOverflowMyOnlyHope
Jak pamiętam, LinkedTreeMap jest zdefiniowany w nowej bibliotece Gson. Możesz sprawdzić tutaj: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
Hoang Nguyen Huu
1
Dla mnie to też działa Map<String,Object> result = gson.fromJson(json , Map.class);. Korzystanie z gson 2.6.2.
Ferran Maylinch
12

Miałem dokładnie to samo pytanie i znalazłem się tutaj. Miałem inne podejście, które wydaje się znacznie prostsze (może nowsze wersje gson?).

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

z następującym jsonem

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

Następujące

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

wyjścia

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

Możesz dynamicznie sprawdzać za pomocą instanceof podczas nawigacji swoim jsonObject. Coś jak

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

To działa dla mnie, więc musi działać dla ciebie ;-)

krico
źródło
6

Poniżej obsługiwane jest od 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));
}
Leon
źródło
3

Spróbuj, to zadziała. Użyłem go do 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;
}

Zamień KioskStatusResource na klasę i liczbę całkowitą na klasę kluczową.

R4U
źródło
Działa to dla mnie po tym, jak HashMap zgłosił wyjątek LinkedTreeMap.
newfivefour
2

Oto, czego używałem:

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
źródło
2

Oto linijka, która to zrobi:

HashMap<String, Object> myMap =
   gson.fromJson(yourJson, new TypeToken<HashMap<String, Object>>(){}.getType());
Ab ِ
źródło
tak, jest to jedna linia, ale należy pamiętać, że new TypeToken<HashMap<String, Object>>(){}stworzy nową wewnętrzną
podklasę
1

Rozwiązałem podobny problem z niestandardowym JsonDeSerializer. Starałem się, aby było to trochę ogólne, ale wciąż za mało. Jest to jednak rozwiązanie, które odpowiada moim potrzebom.

Przede wszystkim musisz wdrożyć nowy JsonDeserializer dla obiektów mapy.

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

Metoda deserializacji będzie wyglądać podobnie do tej:

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

Wadą tego rozwiązania jest to, że klucz mojej mapy jest zawsze typu „Ciąg”. Jednak zmieniając niektóre rzeczy, ktoś może uczynić to ogólnym. Ponadto muszę powiedzieć, że klasa wartości powinna zostać przekazana do konstruktora. Więc metodagetMyType() w moim kodzie zwraca typ wartości mapy, który został przekazany w konstruktorze.

Możesz odnieść się do tego postu Jak napisać niestandardowy deserializator JSON dla Gson? aby dowiedzieć się więcej o niestandardowych deserializatorach.

nikkatsa
źródło
1

Zamiast tego możesz użyć tej klasy :) (obsługuje nawet listy, listy zagnieżdżone i 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;
    }
}

Aby przekonwertować ciąg JSON na skrót, użyj tego:

HashMap<String, Object> hashMap = new HashMap<>(Utility.jsonToMap(response)) ;
Natesh bhat
źródło
0

To bardziej dodatek do odpowiedzi Kevina Dolana niż pełna odpowiedź, ale miałem problem z wyodrębnieniem tego typu z liczby. To jest moje rozwiązanie:

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());
  }
}
Łukasz
źródło
-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;

}
użytkownik2918406
źródło
-3

JSONObject zwykle używa HashMapwewnętrznie do przechowywania danych. Możesz więc użyć go jako mapy w swoim kodzie.

Przykład,

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
źródło
12
To jest z json-lib, nie gson!
Ammar
-3

Użyłem tego kodu:

Gson gson = new Gson();
HashMap<String, Object> fields = gson.fromJson(json, HashMap.class);
mortalis
źródło
Daje mi to niesprawdzone ostrzeżenie o konwersji.
Linia