Вопрос-ответ

How can I convert JSON to a HashMap using Gson?

Как я могу преобразовать JSON в хэш-карту с помощью Gson?

Я запрашиваю данные с сервера, который возвращает данные в формате JSON. Преобразование хэш-карты в 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.

Переведено автоматически
Ответ 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);
Ответ 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());
Ответ 3

Я знаю, что это довольно старый вопрос, но я искал решение для общей десериализации вложенного JSON в Map<String, Object> и ничего не нашел.

То, как работает мой десериализатор yaml, по умолчанию для объектов JSON используется значение Map<String, Object>, когда вы не указываете тип, но gson, похоже, этого не делает. К счастью, вы можете выполнить это с помощью пользовательского десериализатора.

Я использовал следующий десериализатор для естественной десериализации чего угодно, по умолчанию заменив JsonObjects на Map<String, Object> и 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, целое число или Long, и, вероятно, могло бы быть лучше или, по крайней мере, упрощено, если вы согласны с получением BigDecimals, которые, как я полагаю, используются по умолчанию.

Вы можете зарегистрировать этот адаптер следующим образом:

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

А затем вызвать это как:

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

Я не уверен, почему это не поведение по умолчанию в gson, поскольку оно есть в большинстве других полуструктурированных библиотек сериализации...

Ответ 4

С Google Gson 2.7 (возможно, и более ранних версий, но я тестировал с текущей версией 2.7) это так же просто, как:

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

Который возвращает Map типа com.google.gson.internal.LinkedTreeMap и рекурсивно работает с вложенными объектами, массивами и т.д.

Я запустил пример 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}
java json