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.


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

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

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 ...


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}

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

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


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

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.


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

2

Berikut ini adalah satu-liner yang akan melakukannya:

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

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.


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

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

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

}

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

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

Ini memberi saya peringatan konversi yang tidak dicentang.
Jalur
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.