ฉันจะแปลง JSON เป็น HashMap โดยใช้ Gson ได้อย่างไร


286

ฉันขอข้อมูลจากเซิร์ฟเวอร์ที่ส่งคืนข้อมูลในรูปแบบ 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


23
Map<String,Object> result = new Gson().fromJson(json, Map.class);ทำงานร่วมกับ gson 2.6.2
Ferran Maylinch

1
วิธีการแปลงกลับมา? ฉันหมายถึงจากแผนที่ไปยัง Json Array
K.Sopheak

คำตอบ:


471

ไปเลย:

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
หนึ่งที่ดี แต่ฉันทำไม่ได้เช่นการใช้TypeToken- มันไม่หล่อภายในโดยปริยาย
AlikElzin-kilaka

ส่งไปที่แผนที่ <> คุณทำให้ฉันหงุดหงิด!
เด็กซ์เตอร์

1
json นั้นถูกต้องในตัวอย่างหรือไม่?
Evan Kairuz

@EvanKairuz ไม่ไม่ใช่ มันควรจะเป็น{"k1":"apple","k2":"orange"}
Vadim Kotov

จะทำอย่างไรถ้าฉันต้องการแปลงสตริงซึ่งจริงๆแล้วเป็นอาร์เรย์
Ravi Yadav

111

รหัสนี้ใช้งานได้:

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
สิ่งนี้จะแปลง ints ให้ลอยก่อนที่จะเปลี่ยนเป็นสตริง แต่จะทำงานเพื่อแปลง JSON เป็นแผนที่เพื่อการเปรียบเทียบ
louielouie

12
ใช้งานได้ดีสำหรับฉัน แต่ฉันเปลี่ยนแผนที่เป็นMap<String, Object>เพราะหาก json ไม่เพียง แต่เป็นข้อ จำกัด ที่คุณได้รับข้อผิดพลาด
Moshe Shaham

5
สิ่งนี้ทำให้เกิดความประทับใจที่ผิด TypeTokenวิธีการแก้ปัญหาที่ถูกต้องสำหรับประเภทแปรเป็น
Sotirios Delimanolis

นี่จะเป็นคำตอบทั่วไปสำหรับทุกประเภท แต่เป็นเรื่องแปลกเล็กน้อย
ลีออน

76

ฉันรู้ว่านี่เป็นคำถามที่ค่อนข้างเก่า แต่ฉันกำลังค้นหาวิธีแก้ปัญหาโดยทั่วไปทำให้หมดความรู้สึก JSON ซ้อนกันเพื่อ Map<String, Object>และไม่พบอะไรเลย

วิธีการทำงานของ deserializer yaml ของฉันมันเริ่มต้นวัตถุ JSON Map<String, Object>เมื่อคุณไม่ได้ระบุประเภท แต่ gson ดูเหมือนจะไม่ทำเช่นนี้ โชคดีที่คุณสามารถทำได้ด้วย deserializer ที่กำหนดเอง

ฉันใช้ตัว deserializer ต่อไปนี้เพื่อทำให้เป็น deserialize ตามธรรมชาติโดยปริยายJsonObjects Map<String, Object>และJsonArrays เป็นObject[]s โดยที่เด็กทุกคนถูก 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;
  }
}

ความยุ่งเหยิงใน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 เนื่องจากอยู่ในไลบรารีการทำให้เป็นอันดับกึ่งโครงสร้างอื่น ๆ ส่วนใหญ่ ...


1
... ถึงแม้ว่าฉันไม่แน่ใจว่าจะทำอย่างไรกับวัตถุที่ฉันได้รับคืน ดูเหมือนจะไม่สามารถใช้เป็นสตริงได้แม้ว่าฉันจะรู้ว่ามันเป็นสตริง
Matt Zukowski

1
Aha! เคล็ดลับคือการเรียก deserializer แบบเรียกซ้ำมากกว่าการเรียก context.deserialize ()
Matt Zukowski

1
คุณจะมีรหัสแมตต์บ้างไหม? ฉันพยายามที่จะทำการเปลี่ยนแปลงใน deserializer แต่ฉันไม่เห็นจุดของคุณจริงๆ
Romain Piel

5
ตอนนี้ Gson โดยค่าเริ่มต้นดูเหมือนจะมีพฤติกรรมที่ Kevin Dolan กำลังทำอยู่ในข้อมูลโค้ดของเขา
eleotlecram

1
@SomeoneSomewhere ดูได้รับการยอมรับคำตอบที่นี่stackoverflow.com/questions/14944419/gson-to-hashmap
MY

32

ด้วย Gson 2.7 ของ Google (อาจเป็นรุ่นก่อนหน้าด้วย แต่ฉันทดสอบกับรุ่นปัจจุบัน 2.7) มันง่ายเหมือน:

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

ซึ่งส่งกลับMapประเภทcom.google.gson.internal.LinkedTreeMapและทำงานซ้ำบนวัตถุที่ซ้อนกันอาร์เรย์ ฯลฯ

ฉันวิ่งตัวอย่าง OP อย่างนั้น (แทนที่ double- ด้วย single-quotes และลบ 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);

และได้ผลลัพธ์ต่อไปนี้:

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

อัปเดตสำหรับ Gson lib ใหม่:
ตอนนี้คุณสามารถแยกวิเคราะห์ Json ไปยัง Map โดยตรงได้แล้ว แต่คุณควรระวังในกรณีที่คุณพยายามแจง Json ให้Map<String, Object>พิมพ์: มันจะทำให้เกิดข้อยกเว้น ในการแก้ไขปัญหานี้ให้ประกาศผลตามLinkedTreeMapประเภท ตัวอย่างด้านล่าง:

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

ฉันจะนำเข้า LinkedTreeMap จากที่ไหน ฉันหามันไม่พบในรหัส Gson
HelpMeStackOverflowMyOnlyHope

ดังที่ฉันจำได้ว่า LinkedTreeMap ถูกกำหนดไว้ใน Gson lib ใหม่ คุณสามารถตรวจสอบได้ที่นี่: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
Hoang Nguyen Huu

1
Map<String,Object> result = gson.fromJson(json , Map.class);สำหรับฉันมันทำงานยังมี ใช้ gson 2.6.2
Ferran Maylinch

12

ฉันมีคำถามเดียวกันและจบลงที่นี่ ฉันมีวิธีการที่แตกต่างกันซึ่งดูเหมือนจะง่ายกว่ามาก (อาจเป็น 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 ของคุณ สิ่งที่ต้องการ

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

มันใช้งานได้สำหรับฉันดังนั้นมันต้องใช้กับคุณ ;-)


6

ด้านล่างนี้รองรับตั้งแต่ 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

ลองสิ่งนี้มันจะได้ผล ฉันใช้มันสำหรับ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เป็นคลาสของคุณและจำนวนเต็มเป็นคลาสคีย์ของคุณ


สิ่งนี้ใช้ได้กับฉันหลังจาก HashMap โยนข้อยกเว้น LinkedTreeMap
newfivefour

2

นี่คือสิ่งที่ฉันได้ใช้:

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

นี่คือหนึ่งซับที่จะทำ:

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

ใช่มันเป็นหนึ่งบรรทัด แต่โปรดจำไว้ว่าnew TypeToken<HashMap<String, Object>>(){}จะสร้างคลาสย่อยแบบอินไลน์ใหม่และ linters ทั้งหมดจะส่งคำเตือนอย่างน้อยฉันเดา
Martin Meeser

1

ฉันได้เอาชนะปัญหาที่คล้ายกันกับ Custom JsonDeSerializer ฉันพยายามทำให้มันเป็นเรื่องทั่วไป แต่ก็ยังไม่เพียงพอ มันเป็นวิธีการแก้ปัญหาที่เหมาะกับความต้องการของฉัน

ก่อนอื่นคุณต้องใช้ 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;
    }

ข้อเสียด้วยวิธีนี้คือกุญแจของแผนที่ของฉันคือประเภท "String" เสมอ อย่างไรก็ตามโดยการทำบางสิ่งบางอย่างใครบางคนสามารถทำให้เป็นเรื่องปกติ นอกจากนี้ฉันต้องบอกว่าคลาสของค่าควรส่งผ่านในตัวสร้าง ดังนั้นวิธีการgetMyType()ในรหัสของฉันส่งคืนชนิดของค่าของแผนที่ซึ่งถูกส่งผ่านในตัวสร้าง

คุณสามารถอ้างอิงโพสต์นี้ฉันจะเขียน deserializer JSON ที่กำหนดเองสำหรับ Gson ได้อย่างไร เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ deserializers ที่กำหนดเอง


1

คุณสามารถใช้คลาสนี้แทน :) (จัดการแม้กระทั่งรายการรายการซ้อนและ 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)) ;

0

นี่เป็นภาคผนวกของคำตอบของ Kevin Dolanมากกว่าคำตอบที่สมบูรณ์ แต่ฉันมีปัญหาในการแยกประเภทจากหมายเลข นี่คือทางออกของฉัน:

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 ใช้HashMapภายในเพื่อจัดเก็บข้อมูล ดังนั้นคุณสามารถใช้มันเป็นแผนที่ในรหัสของคุณ

ตัวอย่าง,

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
นี่คือจาก json-lib ไม่ใช่ gson!
Ammar

-3

ฉันใช้รหัสนี้:

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

สิ่งนี้ทำให้ฉันไม่ได้รับคำเตือนการแปลง
บรรทัด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.