相关文章推荐
爱喝酒的哑铃  ·  Net ...·  1 年前    · 
坏坏的饭盒  ·  LLaMA, ChatGLM, ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm requesting data from a server which returns data in the JSON format. Casting a HashMap into JSON when making the request wasn't hard at all but the other way seems to be a little tricky. The JSON response looks like this:

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

What way would be easiest to access this data? I'm using the GSON module.

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); new Gson().fromJson(jsonData, new TypeToken<Map<String, Integer>>(){}.getType()); is converting to Double not Integer ?? roottraveller Aug 26, 2020 at 14:34
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());
                This will convert ints to floats before it turns them into strings, but it will work to convert JSON into maps for comparison purposes.
– louielouie
                Oct 19, 2012 at 21:49
                works great for me, but I changed the map to Map<String, Object> because if the json is not only strings you get an error
– Moshe Shaham
                Jul 13, 2013 at 13:52
                This gives the wrong impression. The correct solution for parameterized types is TypeToken.
– Sotirios Delimanolis
                Jul 26, 2016 at 14:29

I know this is a fairly old question, but I was searching for a solution to generically deserialize nested JSON to a Map<String, Object>, and found nothing.

The way my yaml deserializer works, it defaults JSON objects to Map<String, Object> when you don't specify a type, but gson doesn't seem to do this. Luckily you can accomplish it with a custom deserializer.

I used the following deserializer to naturally deserialize anything, defaulting JsonObjects to Map<String, Object> and JsonArrays to Object[]s, where all the children are similarly deserialized.

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;

The messiness inside the handlePrimitive method is for making sure you only ever get a Double or an Integer or a Long, and probably could be better, or at least simplified if you're okay with getting BigDecimals, which I believe is the default.

You can register this adapter like:

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

And then call it like:

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

I'm not sure why this is not the default behavior in gson, since it is in most other semi-structured serialization libraries...

... although I'm not quite sure what to do now with the Objects I get back. Can't seem to cast them as String even though I know they're strings – Matt Zukowski May 20, 2011 at 1:17 Aha! The trick was the call the deserializer recursively instead of the context.deserialize() call. – Matt Zukowski May 20, 2011 at 1:34 Would you have some code Matt? I'm trying to make the changes on the deserializer but I can't really see your point – Romain Piel Jul 18, 2011 at 16:30 Gson now by default appears to have the behavior that Kevin Dolan is going for in his code snippet. – eleotlecram Jan 25, 2013 at 15:13 @SomeoneSomewhere see accepted answer here stackoverflow.com/questions/14944419/gson-to-hashmap – M.Y. Jul 9, 2014 at 9:36

With google's Gson 2.7 (probably earlier versions too, but I tested with the current version 2.7) it's as simple as:

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

Which returns a Map of type com.google.gson.internal.LinkedTreeMap and works recursively on nested objects, arrays, etc.

I ran the OP example like so (simply replaced double- with single-quotes and removed whitespace):

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

And got the following output:

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}

Update for new Gson lib:
You now can parse nested Json to Map directly, but you should be aware in case you try to parse Json to Map<String, Object> type: it will raise exception. To fix this, just declare the result as LinkedTreeMap type. Example below:

String nestedJSON = "{\"id\":\"1\",\"message\":\"web_didload\",\"content\":{\"success\":1}}";
Gson gson = new Gson();
LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);
                As I remember, LinkedTreeMap is defined in new Gson lib. You can check here: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
– Hoang Nguyen Huu
                Jun 18, 2015 at 9:28
                For me it works also with Map<String,Object> result = gson.fromJson(json , Map.class);. Using gson 2.6.2.
– Ferran Maylinch
                Mar 26, 2016 at 23:36
                For me what worked (thanks to advice above!) is convert nested HashMap<String, Object> (because the TypeToken trick didn't work for me on nested) was to return them just as LinkedTreeMap objects. From there I just iterated over the LinkedTreeMap keys and populated new HashMaps in the loop, as they have the same methods. Don't know why you can't straight cast but met my level of need.
– gcr
                Apr 5, 2021 at 7:09

I had the exact same question and ended up here. I had a different approach that seems much simpler (maybe newer versions of gson?).

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

with the following json

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

The following

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

outputs

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

You could dynamically check using instanceof when navigating your jsonObject. Something like

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

It works for me, so it must work for you ;-)

Below is supported since 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));

Try this, it will worked. I used it for 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;

Replace KioskStatusResource to your class and Integer to your key class.

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;
                yes it is one line but keep in mind that new TypeToken<HashMap<String, Object>>(){} will create a new inline sub-class, and all linters will raise a warning at least I guess
– Martin Meeser
                May 12, 2020 at 14:07

I have overcome a similar problem with a Custom JsonDeSerializer. I tried to make it a bit generic but still not enough. It is a solution though that fits my needs.

First of all you need to implement a new JsonDeserializer for Map objects.

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

And the deserialize method will look similar to this:

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;

The con with this solution, is that my Map's key is always of Type "String". However by chaning some things someone can make it generic. In addition, i need to say, that the value's class should be passed in the constructor. So the method getMyType() in my code returns the type of the Map's values, which was passed in the constructor.

You can reference this post How do I write a custom JSON deserializer for Gson? in order to learn more about custom deserializers.

You can use this class instead :) (handles even lists , nested lists and 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;

This is more of addendum to Kevin Dolan's answer than a complete answer, but I was having trouble extracting the type from the Number. This is my solution:

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

JSONObject typically uses HashMap internally to store the data. So, you can use it as Map in your code.

Example,

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

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.