Сериализация в JSON
— Раз уж ты познакомился с JSON, давай поговорим о нем сегодня подробнее.
— Ок. А где обычно он используется?
— Обычно дело выглядит так. Кто-то (клиент) запрашивает у Java-программы (сервера) данные. Программа создает Java-объекты и заполняет их информацией из базы данных. Затем преобразовывает их в формат понятный запрашивающему (клиенту), например JSON, и отсылает их обратно.
Давай я тебе расскажу, как работать с ним из Java. Собственно, нам понадобятся только две вещи – сериализовать Java-объекты в JSON-формат и десериализовать Java-объекты из формата JSON.
Т.е. JSON – это стандарт транспортировки сообщений/данных от одной программы к другой. Таких стандартов довольно много. Но если программа написана на JavaScript, она обычно старается работать с JSON.
Как ты уже знаешь, в Java есть встроенные стандартные средства сериализации. Но JSON к ним не относится. Поэтому если тебе надо использовать сериализацию объекта в JSON, ты можешь использовать один из популярных фреймворков(библиотек), которые это умеют.
— А чем отличаются различные фреймворки?
— Обычно они отличаются степенью сложности: есть фреймворки, которые умеют делать только самое необходимое, но они очень маленькие и простые. А есть и большие сложные фреймворки, которые могут делать гораздо больше.
Одним из популярных фреймворков считается Jackson. Мы рассмотрим работу с JSON на его примере.
Для начала тебе надо скачать этот фреймворк и добавить его себе в проект. Делать это надо в Intellij IDEA само собой. Загрузить фреймворк можно по ссылке.
Сконвертировать Java-объект в JSON примерно так же просто, как и сериализовать его. Для этого есть специальный класс ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper).
Давай я покажу тебе рабочий пример, а потом мы его разберем:
public static void main(String[] args) throws IOException < //создание объекта для сериализации в JSON Cat cat = new Cat(); cat.name = "Murka"; cat.age = 5; cat.weight = 4; //писать результат сериализации будем во Writer(StringWriter) StringWriter writer = new StringWriter(); //это объект Jackson, который выполняет сериализацию ObjectMapper mapper = new ObjectMapper(); // сама сериализация: 1-куда, 2-что mapper.writeValue(writer, cat); //преобразовываем все записанное во StringWriter в строку String result = writer.toString(); System.out.println(result); >
В строках 4-7 мы создаем объект класса Cat и заполняем его данными.
Строка 10 – создаем объект Writer, куда будем писать строку — JSON представление объекта.
Строка 13 – создаем объект ObjectMapper , который и выполняет всю сериализацию.
Строка 16 – пишем JSON-представление объекта cat в writer .
Строки 19-20 – выводим результат на экран.
Все выглядит довольно просто. Не сложнее родной сериализации в Java.
— А как будет выглядеть десериализация?
— Да почти так же, только короче:
public static void main(String[] args) throws IOException < String jsonString text-viola">"; StringReader reader = new StringReader(jsonString); ObjectMapper mapper = new ObjectMapper(); Cat cat = mapper.readValue(reader, Cat.class); >
Тут еще проще. Берем ObjectMapper и передаем в него строку с JSON или StringReader, а также класс объекта, который надо десериализовать . Вызываем метод readValue, и на выходе получаем готовый Java-объект со всеми данными.
— Ну, точно, как десериализация в Java.
— Почти. К объектам, которые сериализуются/десериализуются в JSON есть несколько требований:
1) поля должны быть видимые: или public или иметь getter’ы и setter’ы;
2) должен быть конструктор по умолчанию (без параметров).
— Ясно. Ожидаемо, в общем. Хотя Java отлично сериализовала и private поля.
— Так то — Java. У нее есть доступ к скрытым данным. От себя не утаишь.
Тут есть еще третий аспект. Надеюсь, ты обратил внимание на аннотацию @JsonAutoDetect в классе Cat?
— Ага. Как раз хотел спросить – что это такое.
— Это аннотации – служебная информация для фреймворка Jackson. Можно очень гибко управлять результатом сериализации в JSON формат, расставляя правильные аннотации.
— Круто! А что за аннотации есть?
Аннотация | Описание |
---|---|
@JsonAutoDetect | Ставится перед классом. Помечает класс как готовый к сериализациив JSON. |
@JsonIgnore | Ставится перед свойством. Свойство игнорируется при сериализации. |
@JsonProperty | Ставится перед свойством или getter’ом или setter’ом. Позволяет задать другое имя поля при сериализации. |
@JsonPropertyOrder | Ставится перед классом. Позволяет задать порядок полей для сериализации. |
— Есть много. Но не сейчас. Сейчас давай немного переделаем наш первый пример:
public static void main(String[] args) throws IOException < Cat cat = new Cat(); cat.name = "Murka"; cat.age = 5; cat.weight = 4; StringWriter writer = new StringWriter(); ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(writer, cat); String result = writer.toString(); System.out.println(result); >
Код остался тот же, но я поменяла аннотации: указала другое имя полю name — имя alias. А также отметила поле weight как Ignore, в результате JSON объекта поменялся.
— Хорошо, что можно так всего настраивать – думаю, мне это обязательно пригодится.
А десериализация поймет, как с этим работать? При десериализации из JSON в Java-объект, значение поля alias будет занесено в name объекта Cat?
— Да, десериализация будет работать как надо. Она умная.
Спасибо за такую интересную лекцию, Элли.
Serialize java object with GSON
In order to get the result you desire, you need to write the serializer like this:
public static class PersonSerializer implements JsonSerializer < public JsonElement serialize(final Person person, final Type type, final JsonSerializationContext context) < JsonObject result = new JsonObject(); result.add("id", new JsonPrimitive(person.getId())); result.add("name", new JsonPrimitive(person.getName())); Person parent = person.getParent(); if (parent != null) < result.add("parent", new JsonPrimitive(parent.getId())); >return result; > >
Person p = new Person(1, "Joe", new Person(2, "Mike")); com.google.gson.Gson gson = new GsonBuilder().registerTypeAdapter(Person.class, new PersonSerializer()) .create(); System.out.println(gson.toJson(p));
import java.lang.reflect.Type; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; public class GsonSimpleTest < public static class Person < public int id; public String name; public Person parent; public Person(final int id, final String name) < super(); this.id = id; this.name = name; >public Person(final int id, final String name, final Person parent) < super(); this.id = id; this.name = name; this.parent = parent; >public int getId() < return id; >public void setId(final int id) < this.id = id; >public String getName() < return name; >public void setName(final String name) < this.name = name; >public Person getParent() < return parent; >public void setParent(final Person parent) < this.parent = parent; >> public static class PersonSerializer implements JsonSerializer < public JsonElement serialize(final Person person, final Type type, final JsonSerializationContext context) < JsonObject result = new JsonObject(); result.add("id", new JsonPrimitive(person.getId())); result.add("name", new JsonPrimitive(person.getName())); Person parent = person.getParent(); if (parent != null) < result.add("parent", new JsonPrimitive(parent.getId())); >return result; > > public static void main(final String[] args) < Person p = new Person(1, "Joe", new Person(2, "Mike")); com.google.gson.Gson gson = new GsonBuilder().registerTypeAdapter(Person.class, new PersonSerializer()) .create(); System.out.println(gson.toJson(p)); >>