конвертировать JSON в карту с помощью Gson

Конвертировать JSON в карту с помощью Gson

1. Вступление

In this quick tutorial, we’ll learn how to convert a JSON string to a Map using Gson from Google.

Мы рассмотрим три разных подхода к достижению этой цели и обсудим их плюсы и минусы на некоторых практических примерах.

2. ПроходитMap.class

В общем,Gson provides the following API in its Gson class to convert a JSON string to an object:

public  T fromJson(String json, Class classOfT) throws JsonSyntaxException;

Из подписи очень ясно, что второй параметр - это класс объекта, который мы собираемся анализировать с помощью JSON. В нашем случае это должно бытьMap.class:

String jsonString = "{'employee.name':'Bob','employee.salary':10000}";
Gson gson = new Gson();
Map map = gson.fromJson(jsonString, Map.class);
Assert.assertEquals(2, map.size());
Assert.assertEquals(Double.class, map.get("employee.salary").getClass());

При таком подходе лучше всего определить тип значения для каждого свойства.

Например, числа будут преобразованы вDoubles,true andfalse вBoolean, а объекты вLinkedTreeMaps.

Однако, если есть повторяющиеся ключи, принуждение не удастся и выдастJsonSyntaxException.

И из-заtype erasure, мы также не сможем настроить это поведение принуждения. Итак, если нам нужно указать типы ключей или значений, нам понадобится другой подход.

3. ИспользуяTypeToken

To overcome the problem of type-erasure for the generic types, Gson has an overloaded version of the API:

public  T fromJson(String json, Type typeOfT) throws JsonSyntaxException;

Мы можем построитьMap с его параметрами типа, используя Gson'sTypeToken. The TypeToken class returns an instance of ParameterizedTypeImpl that preserves the type of the key and value even at runtime:

String jsonString = "{'Bob' : {'name': 'Bob Willis'},"
  + "'Jenny' : {'name': 'Jenny McCarthy'}, "
  + "'Steve' : {'name': 'Steven Waugh'}}";
Gson gson = new Gson();
Type empMapType = new TypeToken>() {}.getType();
Map nameEmployeeMap = gson.fromJson(jsonString, empMapType);
Assert.assertEquals(3, nameEmployeeMap.size());
Assert.assertEquals(Employee.class, nameEmployeeMap.get("Bob").getClass());

Теперь, если мы построим наш типMap какMap<String, Object>, то синтаксический анализатор по-прежнему будет работать по умолчанию, как мы видели в предыдущем разделе.

Конечно, это все еще прибегает к Gson для принуждения примитивных типов. Тем не менее, они также могут быть настроены.

4. Использование пользовательскогоJsonDeserializer

Когда нам нужен детальный контроль над построением нашего объектаMap, мы можем реализовать собственный десериализатор типаJsonDeserializer<Map>.

Чтобы увидеть пример, предположим, что наш JSON содержит имя сотрудника в качестве ключа и дату его приема на работу в качестве значения. Далее, предположим, что формат даты -yyyy/MM/dd, что не является стандартным форматом дляGson.

Мы можем настроить Gson для анализа нашей карты по-другому, затем, реализовавJsonDeserializer:

public class StringDateMapDeserializer implements JsonDeserializer> {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");

    @Override
    public Map deserialize(JsonElement elem,
          Type type,
          JsonDeserializationContext jsonDeserializationContext) {
        return elem.getAsJsonObject()
          .entrySet()
          .stream()
          .filter(e -> e.getValue().isJsonPrimitive())
          .filter(e -> e.getValue().getAsJsonPrimitive().isString())
          .collect(
            Collectors.toMap(
              Map.Entry::getKey,
              e -> formatDate(e.getValue())));
    }

    private Date formatDate(Object value) {
        try {
            return format(value.getAsString());
        } catch (ParseException ex) {
            throw new JsonParseException(ex);
        }
    }
}

Теперь мы должны зарегистрировать его вGsonBuilder для нашего целевого типаMap<String, Date> и построить настраиваемый объектGson.

Когда мы вызываем APIfromJson для этого объектаGson, синтаксический анализатор вызывает настраиваемый десериализатор и возвращает желаемый экземплярMap:

String jsonString = "{'Bob': '2017-06-01', 'Jennie':'2015-01-03'}";
Type type = new TypeToken>(){}.getType();
Gson gson = new GsonBuilder()
  .registerTypeAdapter(type, new StringDateMapDeserializer())
  .create();
Map empJoiningDateMap = gson.fromJson(jsonString, type);
Assert.assertEquals(2, empJoiningDateMap.size());
Assert.assertEquals(Date.class, empJoiningDateMap.get("Bob").getClass());

Эта тактика также полезна, когда наша карта может содержать неоднородные значения, и у нас есть четкое представление о том, сколько может быть различных типов значений.

Чтобы узнать больше о настраиваемом десериализаторе вGson, просмотритеGson Deserialization Cookbook.

5. Заключение

В этой короткой статье мы узнали о нескольких способах построения карты из строки в формате JSON. И мы также обсудили правильные варианты использования этих вариантов.

Исходный код примеров доступенover on GitHub.