Bagaimana saya bisa mengonversi JSON ke HashMap menggunakan Gson?

286

Saya meminta data dari server yang mengembalikan data dalam format JSON. Casting HashMap ke JSON saat membuat permintaan itu tidak sulit sama sekali tetapi sebaliknya tampaknya sedikit rumit. Respons JSON terlihat seperti ini:

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

Cara apa yang paling mudah untuk mengakses data ini? Saya menggunakan modul GSON.

Mridang Agarwalla
sumber
23
Map<String,Object> result = new Gson().fromJson(json, Map.class);bekerja dengan gson 2.6.2.
Ferran Maylinch
1
bagaimana cara mengkonversi kembali? Maksud saya dari Map to Json Array.
K.Sopheak

Jawaban:

471

Ini dia:

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);
menghargai
sumber
6
Bagus tapi saya tidak suka menggunakan TypeToken- itu tidak pengecoran implisit di dalam.
AlikElzin-kilaka
Bercak ke Peta <>, kamu mengakhiri frustrasi ku!
Dexter
1
Apakah itu json yang valid dalam contoh?
Evan Kairuz
@ EvanKairuz Tidak, tidak. Seharusnya{"k1":"apple","k2":"orange"}
Vadim Kotov
bagaimana jika saya perlu mengonversi string yang sebenarnya merupakan array
Ravi Yadav
111

Kode ini berfungsi:

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());
malaikat
sumber
1
Ini akan mengkonversi int menjadi float sebelum mengubahnya menjadi string, tetapi akan berfungsi untuk mengubah JSON menjadi peta untuk tujuan perbandingan.
louielouie
12
bekerja sangat baik untuk saya, tapi saya mengubah peta menjadi Map<String, Object>karena jika json tidak hanya string Anda mendapatkan kesalahan
Moshe Shaham
5
Ini memberi kesan yang salah. Solusi yang tepat untuk tipe parameter adalah TypeToken.
Sotirios Delimanolis
Ini akan menjadi solusi umum untuk semua jenis, tetapi sedikit tidak umum.
Leon
76

Saya tahu ini adalah pertanyaan yang cukup lama, tapi saya sedang mencari solusi untuk deserialize umum bersarang JSON ke Map<String, Object>, dan tidak menemukan apa pun.

Cara deserializer yaml saya berfungsi, default objek JSON Map<String, Object>ketika Anda tidak menentukan tipe, tetapi gson tampaknya tidak melakukan ini. Untungnya Anda dapat mencapainya dengan deserializer khusus.

Saya menggunakan deserializer berikut untuk secara alami deserialize apa pun, default JsonObjects ke Map<String, Object>dan JsonArrays ke Object[]s, di mana semua anak deserialized sama.

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

Kekacauan dalam handlePrimitivemetode ini adalah untuk memastikan Anda hanya mendapatkan Double atau Integer atau Long, dan mungkin bisa lebih baik, atau setidaknya disederhanakan jika Anda baik-baik saja dengan mendapatkan BigDecimal, yang saya percaya adalah default.

Anda dapat mendaftarkan adaptor ini seperti:

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

Dan kemudian menyebutnya seperti:

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

Saya tidak yakin mengapa ini bukan perilaku default di gson, karena itu di sebagian besar perpustakaan serialisasi semi-terstruktur ...

Kevin Dolan
sumber
1
... walaupun saya tidak yakin apa yang harus saya lakukan sekarang dengan Objek yang saya dapatkan kembali. Sepertinya tidak bisa
menyebut
1
Aha! Kuncinya adalah panggilan deserializer secara rekursif, bukan konteks. Panggilan () panggilan.
Matt Zukowski
1
Apakah Anda memiliki kode Matt? Saya mencoba untuk membuat perubahan pada deserializer tapi saya tidak bisa benar-benar mengerti maksud Anda
Romain Piel
5
Gson sekarang secara default tampaknya memiliki perilaku yang akan dilakukan Kevin Dolan dalam cuplikan kodenya.
eleotlecram
1
@ Seseorang di suatu tempat melihat jawaban yang diterima di sini stackoverflow.com/questions/14944419/gson-to-hashmap
MY
32

Dengan google's Gson 2.7 (mungkin versi yang lebih lama juga, tapi saya diuji dengan versi 2.7 saat ini) sesederhana:

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

Yang mengembalikan Maptipe com.google.gson.internal.LinkedTreeMapdan bekerja secara rekursif pada objek bersarang, array, dll.

Saya menjalankan contoh OP seperti itu (cukup diganti dengan tanda kutip tunggal dan spasi kosong dihapus):

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

Dan mendapat hasil sebagai berikut:

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
sumber
25

Pembaruan untuk lib Gson baru:
Anda sekarang dapat mem-parsing Json bersarang ke Peta secara langsung, tetapi Anda harus berhati-hati jika Anda mencoba menguraikan Json untuk Map<String, Object>mengetik: itu akan memunculkan pengecualian. Untuk memperbaikinya, cukup deklarasikan hasilnya sebagai LinkedTreeMaptipe. Contoh di bawah ini:

String nestedJSON = "{"id":"1","message":"web_didload","content":{"success":1}};
Gson gson = new Gson();
LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);
Hoang Nguyen Huu
sumber
Di mana saya mengimpor LinkedTreeMap? Saya tidak dapat menemukannya di kode Gson.
HelpMeStackOverflowMyOnlyHope
Seingat saya, LinkedTreeMap didefinisikan dalam lib Gson baru. Anda dapat memeriksa di sini: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
Hoang Nguyen Huu
1
Bagi saya ini juga berfungsi dengan baik Map<String,Object> result = gson.fromJson(json , Map.class);. Menggunakan gson 2.6.2.
Ferran Maylinch
12

Saya memiliki pertanyaan yang sama persis dan berakhir di sini. Saya memiliki pendekatan berbeda yang tampaknya jauh lebih sederhana (mungkin versi gson yang lebih baru?).

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

dengan json berikut

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

Pengikut

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

output

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

Anda dapat secara dinamis memeriksa menggunakan instanceof saat menavigasi jsonObject Anda. Sesuatu seperti

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

Ini bekerja untuk saya, jadi itu harus bekerja untuk Anda ;-)

krico
sumber
6

Di bawah ini didukung sejak 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
sumber
3

Coba ini, ini akan berhasil. Saya menggunakannya untuk 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;
}

Ganti KioskStatusResource ke kelas Anda dan Integer ke kelas utama Anda.

R4U
sumber
Ini bekerja untuk saya setelah HashMap melemparkan pengecualian LinkedTreeMap.
newfivefour empat
2

Inilah yang saya gunakan:

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
sumber
2

Berikut ini adalah satu-liner yang akan melakukannya:

HashMap<String, Object> myMap =
   gson.fromJson(yourJson, new TypeToken<HashMap<String, Object>>(){}.getType());
Ab ِ
sumber
ya itu satu baris tetapi perlu diingat bahwa new TypeToken<HashMap<String, Object>>(){}akan membuat sub-kelas inline baru, dan semua linter akan memunculkan peringatan setidaknya saya kira
Martin Meeser
1

Saya telah mengatasi masalah serupa dengan Custom JsonDeSerializer. Saya mencoba membuatnya agak generik tetapi masih belum cukup. Ini adalah solusi yang sesuai dengan kebutuhan saya.

Pertama-tama Anda perlu mengimplementasikan JsonDeserializer baru untuk objek Map.

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

Dan metode deserialize akan terlihat mirip dengan ini:

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

Yang cocok dengan solusi ini, adalah bahwa kunci Peta saya selalu bertipe "String". Namun dengan menyanyikan beberapa hal seseorang dapat membuatnya generik. Selain itu, saya perlu mengatakan, bahwa kelas nilai harus diteruskan dalam konstruktor. Jadi metode getMyType()dalam kode saya mengembalikan tipe nilai-nilai Peta, yang diteruskan dalam konstruktor.

Anda dapat mereferensikan pos ini Bagaimana cara menulis deserializer JSON khusus untuk Gson? untuk mempelajari lebih lanjut tentang deserializers khusus.

nikkatsa
sumber
1

Anda dapat menggunakan kelas ini sebagai gantinya :) (menangani daftar genap, daftar bersarang, dan 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;
    }
}

Untuk mengonversi string JSON Anda ke hashmap gunakan ini:

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

Ini lebih merupakan tambahan untuk jawaban Kevin Dolan daripada jawaban yang lengkap, tapi saya kesulitan mengekstraksi tipe dari Number. Ini solusi saya:

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

}
pengguna2918406
sumber
-3

JSONObject biasanya menggunakan HashMapinternal untuk menyimpan data. Jadi, Anda bisa menggunakannya sebagai Peta dalam kode Anda.

Contoh,

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
sumber
12
Ini dari json-lib, bukan gson!
Ammar
-3

Saya menggunakan kode ini:

Gson gson = new Gson();
HashMap<String, Object> fields = gson.fromJson(json, HashMap.class);
mortalis
sumber
Ini memberi saya peringatan konversi yang tidak dicentang.
Jalur