Javenue logo

Javenue

Программирование на Java

Информационные технологии

Обзор Gson - работаем с JSON в Java

JSON, что означает JavaScript Object Notation, - это текстовый формат обмена данными, который легко читается человеком и в то же время является компактным (в отличии от того же XML формата).

JSON произошел от javascript и очень часто используется в веб-программировании при обмене данными между веб-браузером и сервером. В самом javascript каждый валидный json объект может быть легко десериализован с помощью встроенной функции eval().

Вообще, о самом формате JSON в Интернете написано более чем достаточно, ну а в этой статье я хочу рассмотреть бибилиотеку Gson для сериализации и десериализации java объектов в JSON. Полный код примеров из статьи, оформленных в виде тест кейсов, можно найти на GitHub по этой ссылке - GsonTest.java.

А вот что нас сегодня ожидает:

Обзор библиотеки Gson

Gson - это небольшая java библиотека, которая позволяет конвертировать java объекты в их JSON представление, равно как и создавать объекты на основании их json представления.

Изначально Gson был разработан в Google и использовался в нескольких внтуренних проектах. Через некоторое время было принято решение отдать библиотеку в open-source, чтобы она и дальше развивалась.

Основным классом библиотеки есть одноименный класс Gson. Для того, чтобы создать экземпляр класса нужно воспользоваться одним из двух способов:

  • Gson gson = new Gson();
  • Gson gson = new GsonBuilder().create();

Первый способ создаст инстанс класса с настройками по умолчанию, а второй способ позволит применить некоторые настройки. О настройках расскажу чуть ниже.

Основные методы, которые используются для сериализации и десериализации java-объектов, называются toJson и fromJson.

Сериализация и десериализация в Gson

Начнем с чего-нибудь попроще:

Gson gson = new Gson();
gson.toJson(123);               // 123
gson.toJson("hello");           // "hello"
gson.toJson(Long.valueOf(10));  // 10

Это так называемы примитивы JSON. А вот так их можно десериализовать:

Integer integer = gson.fromJson("1", int.class);
String string = gson.fromJson("\"world\"", String.class);
Boolean bool = gson.fromJson("true", Boolean.class);

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

Идем дальше. Вот таким образом можно сериализовать и десеарелизовать массив:

String string = gson.toJson(new int[] { 10, 100 }); // [10,100]
int[] array = gson.fromJson("[10,100]", int[].class)

С объектами, которые в качестве полей содержат строки и примитивы, все тоже достаточно просто. Допустим, у нас в приложении описан следующий класс:

public static class Entity {
    volatile int id;
    String name;
    transient long random;

    public Entity(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

И экземпляр класса созданный таким способом:

Entity entity = new Entity(100, "name");
entity.random = 1234;

Смотрим, что получается:

String json = gson.toJson(entity); // {"id":100,"name":"name"}
Entity read = gson.fromJson(json, Entity.class);
System.out.println(read.random);   // 0

Обратите внимание, что при сериализации значение поля random не было сохранено. Все дело в том, что поведение библиотеки по-умолчанию не сериализует поля, помеченные модификатором transient. О том, как изменить это поведение, читайте в разделе про GsonBuilder.

Работа с коллекциями

Сериализация коллекций, таких как ArrayList, LinkedList, HashSet, TreeMap и других, реализована таким образом:

  • метод toJson для Collection вернет массив объектов или примитивов;
  • метод toJson для Map вернет ассоциативный массив.

С десериализацией все немного сложнее. Рассмотрим следующий пример:

Map<String, Integer> map = new LinkedHashMap<>();
map.put("USD", 123);
map.put("EUR", 321);

String json = gson.toJson(map);

Type type = new TypeToken<Map<String, Integer>>(){}.getType();
Map<String, Integer> read = gson.fromJson(json, type);

Обратите внимание как мы определили тип для коллекции при десериализации. К сожалению, сделать это как-то проще не получится, c'est la vie...

Допустим, вам необходимо конвертировать коллекцию, содержащую объекты различных типов. В этом случае с сериализацией проблем не возникнет, например:

Collection collection = new ArrayList();
collection.add("string");
collection.add(10);
collection.add(new Entity(11, "text"));
gson.toJson(collection); // ["string",10,{"id":11,"name":"text"}]

А вот десереализовать такую коллекцию не получится, так как Gson не сможет найти правильные соответствия для типов данных.

Одним из самых хороших решений этой проблемы будет использование низкоуровневого API - классы JsonElement, JsonPrimitive, JsonObject и так далее. Некоторое представление о том, как это сделать, вы сможете получить в следующем разделе статьи.

Определяем свои правила конвертации объектов

Gson позволяет разработчикам определять свои собственные правила для сериализации и десериализации объектов. Зарегистрировать их можно с помощью метода registerTypeAdapter().

Допустим, у нас в приложении есть следующий класс:

public static class Custom {
    Date date;
    BigInteger integer;

    public Custom(Date date, BigInteger integer) {
        this.date = date;
        this.integer = integer;
    }
}

Для кастомного сериализатора необходимо реализовать интерфейс JsonSerializer, а для десериализаторв - соответственно JsonDeserializer. Для простоты можно создать один класс, который реализует оба эти интерфейса:

public class CustomConverter implements JsonSerializer<Custom>, JsonDeserializer<Custom>  {
    public JsonElement serialize(Custom src, Type type,
            JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        object.addProperty("date", src.date.getTime());
        object.addProperty("integer", src.integer.toString());
        return object;
    }

    public Custom deserialize(JsonElement json, Type type,
            JsonDeserializationContext context) throws JsonParseException {
        JsonObject object = json.getAsJsonObject();
        Date date = new Date(object.get("date").getAsLong());
        BigInteger integer = new BigInteger(object.get("integer").getAsString());
        return new Custom(date, integer);
    }
}

Зарегистрировать наш класс можно следующим образом:

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Custom.class, new CustomConverter());
Gson gson = builder.create();

Настройки Gson и класс GsonBuilder

В этом разделе я бы хотел мельком рассмотреть несколько настроек класса GsonBuilder.

По умолчанию результат сериализации в json будет компактным, то есть все лишние whitespace символы будут удалены. Это позволит, например, уменьшить траффик при передачи JSON объектов по сети.

Метод setPrettyPrinting у класса GsonBuilder меняет это поведение и сериализует объекты в удобную для человека форму с пробелами и переводами строк. Пример вы можете посмотреть по ссылке приведенной в начале статьи.

Еще одна полезная настройка для GsonBuilder - excludeFieldsWithModifiers. Она позволяет изменить набор несериализуемых полей при конвертации java объектов в JSON. По умолчанию игнорируются только поля с модификатором transient.

Ну вот наверное и все. Вопросы и комментарии приветствуются.


Комментариев: 4

  Выйти

  * для публикации комментариев нужно  

Аарон Махлин:

в статье не хватает ссылки на продолжающую статью

Аарон Махлин:

хотя оказывается следующая статья еще не вышла :) Спасибо тогда за эту))

Антон Пересыпкин:

в статье не хватает ссылки на продолжающую статью

Антон Пересыпкин:

хотя оказывается следующая статья еще не вышла :) Спасибо тогда за эту))