【问题标题】:How can I convert JSON to a HashMap using Gson?如何使用 Gson 将 JSON 转换为 HashMap?
【发布时间】:2011-02-16 06:43:08
【问题描述】:

我正在从以 JSON 格式返回数据的服务器请求数据。在发出请求时将 HashMap 转换为 JSON 一点也不难,但另一种方式似乎有点棘手。 JSON 响应如下所示:

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

什么方法最容易访问这些数据?我正在使用 GSON 模块。

【问题讨论】:

  • Map<String,Object> result = new Gson().fromJson(json, Map.class); 适用于 gson 2.6.2。
  • 如何转换回来?我的意思是从 Map 到 Json Array。

标签: java json dictionary hashmap gson


【解决方案1】:

给你:

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

【讨论】:

  • 不错,但我不喜欢使用TypeToken - 它在内部进行隐式转换。
  • 投射到地图,你结束了我几个小时的挫败感!
  • 示例中的 json 是否有效?
  • @EvanKairuz 不,不是。应该是{"k1":"apple","k2":"orange"}
  • new Gson().fromJson(jsonData, new TypeToken&lt;Map&lt;String, Integer&gt;&gt;(){}.getType()); 正在转换为 Double 而不是 Integer??
【解决方案2】:

此代码有效:

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

【讨论】:

  • 这会将整数转换为浮点数,然后再将它们转换为字符串,但它可以将 JSON 转换为映射以进行比较。
  • 非常适合我,但我将地图更改为 Map&lt;String, Object&gt;,因为如果 json 不仅是字符串,则会出现错误
  • 这给人错误的印象。参数化类型的正确解决方案是TypeToken
  • 这将是所有类型的通用解决方案,但有点不常见。
【解决方案3】:

我知道这是一个相当老的问题,但我正在寻找一种解决方案,将嵌套的 JSON 反序列化为 Map&lt;String, Object&gt;,但一无所获。

我的 yaml 反序列化程序的工作方式是,当您不指定类型时,它会将 JSON 对象默认为 Map&lt;String, Object&gt;,但 gson 似乎没有这样做。幸运的是,您可以使用自定义反序列化器来完成它。

我使用下面的反序列化器自然反序列化任何东西,默认JsonObjects 为Map&lt;String, Object&gt;JsonArrays 为Object[]s,所有的孩子都被类似地反序列化。

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

handlePrimitive 方法内部的混乱是为了确保你只得到一个 Double 或一个 Integer 或一个 Long,如果你可以得到 BigDecimals 可能会更好,或者至少简化,我相信是默认值。

你可以像这样注册这个适配器:

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

然后这样称呼它:

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

我不确定为什么这不是 gson 中的默认行为,因为它在大多数其他半结构化序列化库中...

【讨论】:

  • ...虽然我不太确定现在如何处理我返回的对象。即使我知道它们是字符串,似乎也无法将它们转换为字符串
  • 啊哈!诀窍是递归调用反序列化器而不是 context.deserialize() 调用。
  • 你有一些代码马特吗?我正在尝试对反序列化器进行更改,但我无法真正理解您的观点
  • Gson 现在默认情况下似乎具有 Kevin Dolan 在他的代码 sn-p 中的行为。
  • @SomeoneSomewhere 在此处查看接受的答案stackoverflow.com/questions/14944419/gson-to-hashmap
【解决方案4】:

使用 google 的 Gson 2.7(也可能是更早的版本,但我使用当前版本 2.7 进行了测试)很简单:

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

它返回 com.google.gson.internal.LinkedTreeMap 类型的 Map 并递归地作用于嵌套对象、数组等。

我像这样运行 OP 示例(简单地将双引号替换为单引号并删除空格):

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

得到以下输出:

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}

【讨论】:

    【解决方案5】:

    新 Gson 库的更新:
    您现在可以直接将嵌套的 Json 解析为 Map,但您应该注意,如果您尝试将 Json 解析为 Map&lt;String, Object&gt; 类型:它会引发异常。要解决此问题,只需将结果声明为 LinkedTreeMap 类型。示例如下:

    String nestedJSON = "{\"id\":\"1\",\"message\":\"web_didload\",\"content\":{\"success\":1}}";
    Gson gson = new Gson();
    LinkedTreeMap result = gson.fromJson(nestedJSON , LinkedTreeMap.class);
    

    【讨论】:

    • 从哪里导入 LinkedTreeMap?我在 Gson 代码中找不到它。
    • 我记得,LinkedTreeMap 是在新的 Gson 库中定义的。你可以在这里查看:code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
    • 对我来说它也适用于Map&lt;String,Object&gt; result = gson.fromJson(json , Map.class);。使用 gson 2.6.2。
    • 对我来说有效的方法(感谢上面的建议!)是转换嵌套的HashMap&lt;String, Object&gt;(因为 TypeToken 技巧在嵌套时对我不起作用)是将它们作为 LinkedTreeMap 对象返回。从那里我只是迭代了 LinkedTreeMap 键并在循环中填充了新的 HashMap,因为它们具有相同的方法。不知道为什么你不能直接施法但满足了我的需要。
    【解决方案6】:

    我有完全相同的问题并最终来到这里。我有一个看起来更简单的不同方法(也许是新版本的 gson?)。

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

    使用以下 json

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

    以下

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

    输出

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

    您可以在导航 jsonObject 时使用 instanceof 动态检查。类似的东西

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

    它对我有用,所以它一定对你有用;-)

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

        试试这个,它会起作用的。我将它用于 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;
        }
        

        KioskStatusResource 替换为您的类,将 Integer 替换为您的关键类。

        【讨论】:

        • 在 HashMap 引发 LinkedTreeMap 异常后,这对我有用。
        【解决方案9】:

        这是我一直在使用的:

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

        【讨论】:

          【解决方案10】:

          这是一个可以做到这一点的单线:

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

          【讨论】:

          • 是的,这是一行,但请记住 new TypeToken&lt;HashMap&lt;String, Object&gt;&gt;(){} 将创建一个新的内联子类,并且至少我猜所有的 linter 都会发出警告
          【解决方案11】:

          我已经通过自定义 JsonDeSerializer 克服了类似的问题。我试着让它有点通用,但仍然不够。这是一个适合我需要的解决方案。

          首先你需要为 Map 对象实现一个新的 JsonDeserializer。

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

          而反序列化方法看起来类似于:

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

          这个解决方案的缺点是我的 Map 的键始终是“String”类型。然而,通过改变一些东西,有人可以使它通用。另外,我需要说的是,值的类应该在构造函数中传递。所以我的代码中的方法getMyType()返回了Map的值的类型,它是在构造函数中传递的。

          您可以参考这篇文章How do I write a custom JSON deserializer for Gson? 以了解有关自定义反序列化程序的更多信息。

          【讨论】:

            【解决方案12】:

            你可以改用这个类:)(处理偶数列表、嵌套列表和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;
                }
            }
            

            要将您的 JSON 字符串转换为 hashmap,请使用:

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

            【讨论】:

              【解决方案13】:

              这更像是对Kevin Dolan's answer 的补充,而不是完整的答案,但我无法从数字中提取类型。这是我的解决方案:

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

              【讨论】:

                【解决方案14】:
                 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;
                
                }
                

                【讨论】:

                  【解决方案15】:

                  JSONObject 通常在内部使用HashMap 来存储数据。因此,您可以在代码中将其用作 Map。

                  例子,

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

                  【讨论】:

                  • 这是来自 json-lib,而不是 gson!
                  【解决方案16】:

                  我使用了这个代码:

                  Gson gson = new Gson();
                  HashMap<String, Object> fields = gson.fromJson(json, HashMap.class);
                  

                  【讨论】:

                  • 这给了我未经检查的转换警告。
                  猜你喜欢
                  • 1970-01-01
                  • 2020-07-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多