JSON'u Gson kullanarak bir HashMap'e nasıl dönüştürebilirim?


286

JSON biçiminde veri döndüren bir sunucudan veri talep ediyorum. İsteği yaparken bir HashMap'i JSON'a yayınlamak hiç de zor değildi, ancak diğer yol biraz zor görünüyor. JSON yanıtı şöyle görünür:

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

Bu verilere erişmek için en kolay yol hangisidir? GSON modülünü kullanıyorum.


23
Map<String,Object> result = new Gson().fromJson(json, Map.class);gson ile çalışır 2.6.2.
Ferran Maylinch

1
nasıl dönüştürmek için? Haritadan Json Array'a yani.
K.Sopheak

Yanıtlar:


471

Hadi bakalım:

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
İyi olan ama kullanmaktan hoşlanmıyorum TypeToken- içeride döküm yapılıyor.
AlikElzin-kilaka

Haritaya Yayınlama <>, saatlerimi hayal kırıklığına uğrattın!
Dexter

1
Örnekteki geçerli json mu?
Evan Kairuz

@EvanKairuz Hayır, değil. O olmalı{"k1":"apple","k2":"orange"}
Vadim Kotov

aslında bir dizi olan dize dönüştürmek gerekirse ne
Ravi Yadav

111

Bu kod çalışır:

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
Bu, satırları dizelere dönüştürmeden önce kayan noktalara dönüştürür, ancak karşılaştırma amacıyla JSON'u haritalara dönüştürmeye çalışır.
louielouie

12
benim için harika çalışıyor, ancak haritayı değiştirdim Map<String, Object>çünkü json sadece dizeler değilse bir hata alıyorsunuz
Moshe Shaham

5
Bu yanlış izlenim veriyor. Parametreli tipler için doğru çözüm TypeToken.
Sotirios Delimanolis

Bu, tüm türler için genel bir çözüm olacaktır, ancak biraz nadirdir.
Leon

76

Bu oldukça eski bir soru olduğunu biliyorum, ama ben iç içe JSON a jenerik olarak serileştirmek için bir çözüm arıyordu Map<String, Object>ve hiçbir şey bulunamadı.

Yaml deserializer'ımın çalışma şekli, Map<String, Object>bir tür belirtmediğinizde varsayılan olarak JSON nesnelerini kullanır, ancak gson bunu yapmıyor gibi görünüyor. Neyse ki bunu özel bir deserializer ile başarabilirsiniz.

Ben varsaymak, doğal serisi kaldırılmaya şey şu deserializer kullanılan JsonObjectetmek lar Map<String, Object>ve JsonArrays Object[]tüm çocukların benzer serileştirilemezse edilir 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;
  }
}

handlePrimitiveYöntemin içindeki dağınıklık , yalnızca bir Double veya Tamsayı veya Long elde etmenizi sağlamaktır ve muhtemelen daha iyi olabilir veya en azından varsayılan olan BigDecimals'i almakta sorun yaşıyorsanız en azından basitleştirilebilir.

Bu bağdaştırıcıyı aşağıdaki gibi kaydedebilirsiniz:

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

Ve sonra şöyle deyin:

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

Diğer yarı yapılandırılmış serileştirme kitaplıklarında olduğundan, neden gson'daki varsayılan davranış olmadığından emin değilim ...


1
... geri döndüğüm nesnelerle şimdi ne yapacağımdan tam olarak emin olmasam da. Telli olduklarını
bilsem

1
Aha! Hile, serpiştiricinin context.deserialize () çağrısı yerine özyinelemeli çağrı oldu.
Matt Zukowski

1
Matt kodun olur mu? Deserializer üzerinde değişiklikler yapmaya çalışıyorum ama gerçekten ne
Romain Piel 18:30

5
Gson artık varsayılan olarak Kevin Dolan'ın kod snippet'inde uyguladığı davranışa sahip gibi görünüyor.
eleotlecram

1
@SomeoneSomewhere kabul edilen yanıtı burada görebilirsiniz stackoverflow.com/questions/14944419/gson-to-hashmap
MY

32

Google'ın Gson 2.7'siyle (muhtemelen daha önceki sürümler de, ancak mevcut 2.7 sürümü ile test ettim) şu kadar basit:

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

Hangi Maptür a döndürür com.google.gson.internal.LinkedTreeMapve iç içe geçmiş nesnelerde, dizilerde vb.

Böyle OP örnek koştu (basitçe çift-tek tırnak ve kaldırıldı boşluk ile değiştirildi):

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

Ve aşağıdaki çıktıyı aldım:

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

Yeni Gson lib güncellemesi:
Artık yuvalanmış Json'u doğrudan Harita'ya ayrıştırabilirsiniz, ancak Json'u yazmak için ayrıştırmaya çalıştığınızda farkında olmanız gerekir Map<String, Object>: istisna doğacaktır. Bunu düzeltmek için sonucu LinkedTreeMaptür olarak bildirin. Aşağıdaki örnek:

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

LinkedTreeMap'i nereden alabilirim? Gson kodunda bulamıyorum.
HelpMeStackOverflowMyOnlyHope

Hatırladığım gibi, LinkedTreeMap yeni Gson lib'de tanımlandı. Buradan kontrol edebilirsiniz: code.google.com/p/google-gson/source/browse/trunk/gson/src/main/…
Hoang Nguyen Huu

1
Benim için de işe yarıyor Map<String,Object> result = gson.fromJson(json , Map.class);. Gson kullanımı 2.6.2.
Ferran Maylinch

12

Ben de aynı soruyu sordum ve burada bitirdim. Ben çok daha basit görünüyor farklı bir yaklaşım vardı (belki daha yeni gson sürümleri?).

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

aşağıdaki json ile

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

Devamındaki

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

çıktılar

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

JsonObject öğenizde gezinirken instanceof komutunu kullanarak dinamik olarak kontrol edebilirsiniz. Gibi bir şey

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

Benim için çalışıyor, bu yüzden senin için çalışmalı ;-)


6

Aşağıda gson 2.8.0'dan beri desteklenmektedir

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

Bunu deneyin, işe yarayacak. Hashtable için kullandım .

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

Değiştir KioskStatusResource sınıfınızda ve Tamsayı anahtar sınıfa.


Bu, HashMap bir LinkedTreeMap istisnası attıktan sonra benim için çalıştı.
newfivefour

2

İşte ne kullanıyorum:

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

İşte bunu yapacak bir astar:

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

evet bu bir satır ama new TypeToken<HashMap<String, Object>>(){}yeni bir satır içi alt sınıf yaratacağını unutmayın ve tüm
linters

1

Özel bir JsonDeSerializer ile benzer bir sorunun üstesinden geldim. Ben biraz genel yapmaya çalıştım ama yine de yeterli değil. Gereksinimlerime uygun olsa da bir çözüm.

Her şeyden önce Harita nesneleri için yeni bir JsonDeserializer uygulamanız gerekir.

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

Deserialize yöntemi buna benzer olacaktır:

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

Bu çözüm ile con, Haritamın anahtarı her zaman "Dize" Tipi olmasıdır. Bununla birlikte, bazı şeyleri değiştirerek birisi genel yapabilir. Ayrıca, değerin sınıfı yapıcı geçirilmesi gerektiğini söylemeliyim. Kodumdaki yöntem getMyType(), yapıcıya iletilen Harita değerlerinin türünü döndürür.

Bu gönderiye başvurabilirsiniz Gson için nasıl özel bir JSON deserializer yazabilirim? özel serileştiriciler hakkında daha fazla bilgi edinmek için.


1

Bunun yerine bu sınıfı kullanabilirsiniz :) (hatta listeleri, iç içe listeleri ve json'u işler)

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 dizenizi hashmap biçimine dönüştürmek için şunu kullanın:

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

0

Bu, Kevin Dolan'ın cevabına tam bir cevaptan daha fazla bir eklentidir, ancak Numara'dan türü ayıklamakta sorun yaşıyordum. Bu benim çözümüm:

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 genellikle HashMapverileri depolamak için dahili olarak kullanır . Böylece, kodunuzda Harita olarak kullanabilirsiniz.

Misal,

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
Bu json-lib'den, gson değil!
Ammar

-3

Bu kodu kullandım:

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

Bu bana kontrolsüz dönüşüm uyarısı verir.
Line
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.