Сериализация java в json
Формат JSON, метод toJSON
Допустим, у нас есть сложный объект, и мы хотели бы преобразовать его в строку, чтобы отправить по сети или просто вывести для логирования.
Естественно, такая строка должна включать в себя все важные свойства.
Мы могли бы реализовать преобразование следующим образом:
…Но в процессе разработки добавляются новые свойства, старые свойства переименовываются и удаляются. Обновление такого toString каждый раз может стать проблемой. Мы могли бы попытаться перебрать свойства в нём, но что, если объект сложный, и в его свойствах имеются вложенные объекты? Мы должны были бы осуществить их преобразование тоже.
К счастью, нет необходимости писать код для обработки всего этого. У задачи есть простое решение.
JSON.stringify
JSON (JavaScript Object Notation) – это общий формат для представления значений и объектов. Его описание задокументировано в стандарте RFC 4627. Первоначально он был создан для JavaScript, но многие другие языки также имеют библиотеки, которые могут работать с ним. Таким образом, JSON легко использовать для обмена данными, когда клиент использует JavaScript, а сервер написан на Ruby/PHP/Java или любом другом языке.
JavaScript предоставляет методы:
- JSON.stringify для преобразования объектов в JSON.
- JSON.parse для преобразования JSON обратно в объект.
Например, здесь мы преобразуем через JSON.stringify данные студента:
Метод JSON.stringify(student) берёт объект и преобразует его в строку.
Полученная строка json называется JSON-форматированным или сериализованным объектом. Мы можем отправить его по сети или поместить в обычное хранилище данных.
Обратите внимание, что объект в формате JSON имеет несколько важных отличий от объектного литерала:
- Строки используют двойные кавычки. Никаких одинарных кавычек или обратных кавычек в JSON. Так ‘John’ становится «John» .
- Имена свойств объекта также заключаются в двойные кавычки. Это обязательно. Так age:30 становится «age»:30 .
JSON.stringify может быть применён и к примитивам.
JSON поддерживает следующие типы данных:
- Объекты
- Массивы [ . ]
- Примитивы:
- строки,
- числа,
- логические значения true/false ,
- null .
JSON является независимой от языка спецификацией для данных, поэтому JSON.stringify пропускает некоторые специфические свойства объектов JavaScript.
- Свойства-функции (методы).
- Символьные свойства.
- Свойства, содержащие undefined .
Обычно это нормально. Если это не то, чего мы хотим, то скоро мы увидим, как можно настроить этот процесс.
Самое замечательное, что вложенные объекты поддерживаются и конвертируются автоматически.
Важное ограничение: не должно быть циклических ссылок.
Здесь преобразование завершается неудачно из-за циклической ссылки: room.occupiedBy ссылается на meetup , и meetup.place ссылается на room :
Исключаем и преобразуем: replacer
Полный синтаксис JSON.stringify :
В большинстве случаев JSON.stringify используется только с первым аргументом. Но если нам нужно настроить процесс замены, например, отфильтровать циклические ссылки, то можно использовать второй аргумент JSON.stringify .
Если мы передадим ему массив свойств, будут закодированы только эти свойства.
Здесь мы, наверное, слишком строги. Список свойств применяется ко всей структуре объекта. Так что внутри participants – пустые объекты, потому что name нет в списке.
Давайте включим в список все свойства, кроме room.occupiedBy , из-за которого появляется цикличная ссылка:
Теперь всё, кроме occupiedBy , сериализовано. Но список свойств довольно длинный.
К счастью, в качестве replacer мы можем использовать функцию, а не массив.
Функция будет вызываться для каждой пары (key, value) , и она должна возвращать заменённое значение, которое будет использоваться вместо исходного. Или undefined , чтобы пропустить значение.
В нашем случае мы можем вернуть value «как есть» для всего, кроме occupiedBy . Чтобы игнорировать occupiedBy , код ниже возвращает undefined :
Обратите внимание, что функция replacer получает каждую пару ключ/значение, включая вложенные объекты и элементы массива. И она применяется рекурсивно. Значение this внутри replacer – это объект, который содержит текущее свойство.
Первый вызов – особенный. Ему передаётся специальный «объект-обёртка»: <"": meetup>. Другими словами, первая (key, value) пара имеет пустой ключ, а значением является целевой объект в общем. Вот почему первая строка из примера выше будет «:[object Object]» .
Идея состоит в том, чтобы дать как можно больше возможностей replacer – у него есть возможность проанализировать и заменить/пропустить даже весь объект целиком, если это необходимо.
Форматирование: space
Третий аргумент в JSON.stringify(value, replacer, space) – это количество пробелов, используемых для удобного форматирования.
Ранее все JSON-форматированные объекты не имели отступов и лишних пробелов. Это нормально, если мы хотим отправить объект по сети. Аргумент space используется исключительно для вывода в удобочитаемом виде.
Ниже space = 2 указывает JavaScript отображать вложенные объекты в несколько строк с отступом в 2 пробела внутри объекта:
Параметр space применяется для логирования и красивого вывода.
Пользовательский «toJSON»
Как и toString для преобразования строк, объект может предоставлять метод toJSON для преобразования в JSON. JSON.stringify автоматически вызывает его, если он есть.
Как видим, date (1) стал строкой. Это потому, что все объекты типа Date имеют встроенный метод toJSON , который возвращает такую строку.
Теперь давайте добавим собственную реализацию метода toJSON в наш объект room (2) :
Сериализация java в json
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
JavaRushTasks / 4.JavaCollections / src / com / javarush / task / task33 / task3301 / Solution.java
package com.javarush.task.task33.task3301 ; |
import com.fasterxml.jackson.annotation.JsonAutoDetect ; |
import com.fasterxml.jackson.annotation.JsonSubTypes ; |
import com.fasterxml.jackson.annotation.JsonTypeInfo ; |
import com.fasterxml.jackson.databind.ObjectMapper ; |
import java.io.IOException ; |
import java.io.StringWriter ; |
import java.util.ArrayList ; |
/* |
Первая сериализация в JSON |
НЕОБХОДИМО: подключенные библиотеки Jackson Core, Bind и Annotation версии 2.6.1 |
1) В программе не выполнено основное требование к сериализации в JSON. |
Найди ошибку и исправь. |
2) Расставь правильно JSON аннотации у классов. |
Все данные должны сериализоваться. |
Требования: |
1. Класс Pet должен быть помечен как готовый к сериализации в JSON. |
2. Класс Cat должен быть помечен как готовый к сериализации в JSON. |
3. Класс Dog должен быть помечен как готовый к сериализации в JSON. |
4. Все данные у классов Pet, Cat, Dog должны сериализоваться. |
*/ |
public class Solution < |
public static void main ( String [] args ) throws IOException < |
Cat cat = new Cat (); |
cat . name = » Murka » ; |
cat . age = 5 ; |
cat . weight = 3 ; |
Dog dog = new Dog (); |
dog . name = » Killer » ; |
dog . age = 8 ; |
dog . owner = » Bill Jeferson » ; |
ArrayList Pet > pets = new ArrayList<> (); |
pets . add(cat); |
pets . add(dog); |
StringWriter writer = new StringWriter (); |
convertToJSON(writer, pets); |
System . out . println(writer . toString()); |
// [<"name":"Murka","age":5,"weight":3>,<"name":"Killer","age":8,"owner":"Bill Jeferson">] |
> |
public static void convertToJSON ( StringWriter writer , Object object ) throws IOException < |
ObjectMapper mapper = new ObjectMapper (); |
mapper . writeValue(writer, object); |
> |
/* @JsonTypeInfo(use = JsonTypeInfo. ) |
@JsonSubTypes( < |
@JsonSubTypes.Type(value=Cat. ), |
@JsonSubTypes.Type(value=Dog. ) |
>) */ |
@JsonAutoDetect |
public static class Pet < |
public String name; |
Pet () <> |
> |
@JsonAutoDetect |
public static class Cat extends Pet < |
Cat ()<> |
public int age; |
public int weight; |
> |
@JsonAutoDetect |
public static class Dog extends Pet < |
public int age; |
public String owner; |
public Dog () <> |
> |
> |
- © 2020 GitHub, Inc.
- Terms
- Privacy
- Security
- Status
- Help
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Шпаргалка Java программиста 8. Библиотеки для работы с Json (Gson, Fastjson, LoganSquare, Jackson, JsonPath и другие)
В одной из моих прошлых статей я рассказывал о своем opensorce pet проекте useful-java-links, идея которого собрать как можно больше ссылок на полезные Java библиотеки и фреймворки. У него так же есть подпроект Hello World project идея которого для каждой библиотеки собрать несколько простых примеров её использования.
Проблема программистов в Java мире в том что кроме стандартной библиотеки JDK есть огромное других полезных библиотек, причем переход от одной библиотеки к другой может вызывать проблемы из-за неполной документации, отсутствия простых примеров или даже сложности понять какие зависимости нужно добавить в maven чтобы все запустилось. А на новой работе вполне могут использовать вместо твоей любимой библиотеки ту которую ты не знаешь. Идея моего проекта облегчить изучение и выбор разных библиотек.
Итак, давайте посмотрим какие известные библиотеки есть для работы с JSON в Java…
8. Работа с Json
JSON парсеры
Аналог XPath для JSON
Генерация Java классов из JSON или JSON схемы и JSON валидация
Итак, у нас восемь библиотек для сериализации и десериализации в json, две библиотеки для генерации Java классов по схеме или json файлу, одна библиотека для валидации схемы и два аналога XPath, но для json. Давайте рассмотрим каждую из них.
1. JSON парсеры
Существует три основных способа сериализации и десериализации среди указанных библиотек (от самого простого к самому сложному) и один дополнительный:
- Data bind,
- Tree Model,
- Streaming API,
- (И дополнительный способ) Аналоги XPath,
Давайте рассмотрим с чем их едят:
Data bind самый популярный и простой способ, вы просто указываете класс, который нужно преобразовать в json, может быть часть полей отмечаете аннотациями (а зачастую даже это необязательно), а библиотека сама превращает этот класс и всю его иерархию классов в json. Аналогом при работе с xml будет JAXB (Java Architecture for XML Binding)
Плюсы: наиболее простой из всех, по сути главное реализовать только Java классы, более того можно просто сгенерировать Java классы из json’a или json схемы.
Минусы: скорость и память. Большинство библиотек использует рефлексию и т.п. методы работы с Java классами (хотя не все), что очевидно не очень быстро. К тому же, весь json файл сразу превращается в Java объекты, что может просто исчерпать всю доступную память, если вы попытаетесь обработать очень большой json.
Вывод: если нет проблем с производительностью, памятью и вы не собираетесь обрабатывать многогигабайтные json’ы скорее всего самый лучший способ.
Tree Model — данный парсер представляет json в виде Java классов таких как Node или JsonElement c иерархической структурой, а уже сам программист их обходит и получает из них информацию. Данный способ похож на DOM парсеры в xml.
Плюсы: обычно быстрее первого способа и проще третьего,
Минусы: уступает Data bind по простоте, плюс ряд библиотек способен генерить классы при Data bind, а не использовать рефлексию, в этом случае то что Tree Model будет быстрее не очевидно, к тому же не решается проблема огромных файлов и ограничения памяти.
Streaming API — самый низкоуровневый способ, по сути программист сам вручную разбирает токены json’a. Зато никаких ограничений по памяти и в теории максимальная производительность.
Плюсы: производительность и минимальное потребление памяти,
Минусы: сложность использования,
Плюсы: позволяет быстро получить информацию из json’а по сложным критериям,
Минусы: не очень подходит, когда нужна все информация из json’а, не работает в обратную сторону на формирования json’ов,
1.1 Обзор библиотек
Способ | Fastjson | Gson | LoganSquare | JSON java | Moshi | Ig json parser | Jackson | Genson | JsonPath |
---|---|---|---|---|---|---|---|---|---|
1. Data bind | Да | Да | Да | — | Да | Да | Да | Да | — |
2. Tree Model | — | Да | — | Да | — | — | Да | — | — |
3. Streaming API | — | Да | — | — | — | — | Да | — | — |
4. Аналоги XPath | Да | — | — | — | — | — | — | — | Да |
5. Генерация классов для Data bind* | — | — | Да | — | — | Да | — | — | — |
6. Github’s star | 4851 | 4120 | 2188 | 1937 | 1732 | 921 | 881 | 108 | 849 |
7. Работает со static inner class** | Да | Да | Нет | — | Да | Нет | Да | Да | — |
8. Обязательность аннотаций*** | Нет | Нет | Да | — | Нет | Да | Нет | Нет | — |
По ссылкам на Да можно найти примеры использования.
* — Генерация классов для Data bind позволяет сгенерировать классы на стадии компиляции, что в теории должно давать значительный прирост производительности библиотеки,
** — Работает со static inner class имеет смысл только для случая Data bind, возможно ли сериализация и десериализация для случая статических внутренних классов (не статические внутренние классы сериализовать не рекомендуется),
*** — тоже только для случая Data bind можно ли не использовать аннотации или их использование крайне рекомендуется,
1.2 Простейшие примеры использование Data bind
Для демонстрации работы библиотек будем использовать следующий json:
И следующие Java классы (в разных примерах могут слегка отличаться наличием аннотаций, если они обязательны):
Как можно увидеть, Java классы всего лишь состоять из двух классов Human и Place, в которых храниться сообщение Hi World. Json тоже содержит эти два вложенных объекта.
Примеры использования (Data bind): Способ | Fastjson | Gson | LoganSquare | Moshi | Ig json parser | Jackson | Genson |
---|---|---|---|---|---|---|---|
Инициализация | — | Gson gson = new Gson() | — | Moshi moshi = new Moshi. Builder().build(); JsonAdapter jsonAdapter = moshi.adapter(Human.class) | — | ObjectMapper mapper = new ObjectMapper() | Genson genson = new Genson() |
Из Java в json | JSON.toJSONString(human) | gson.toJson(human) | LoganSquare.serialize(human) | jsonAdapter.toJson(human) | Human__JsonHelper.serializeToJson(human) | mapper.writeValueAsString(human) | genson.serialize(human) |
Из json в Java | JSON.parseObject(jsonString, Human.class) | gson.fromJson(jsonString, Human.class) | LoganSquare.parse(jsonString, Human.class) | jsonAdapter.fromJson(jsonString) | Human__JsonHelper.parseFromJson(jsonString) | mapper.readValue(jsonString, Human.class) | genson.deserialize(jsonString, Human.class) |
Human__JsonHelper — это класс который Ig json parser сгенерировал на этапе компиляции, у LoganSquare так же есть генерации на этапе компиляции, но там классы подключаются «под капотом» внутри LoganSquare.
Конвертация Java объектов в JSON
Конвертация Java объектов есть одним из часто-используемых в разработке WEB-ресурсов. В этом уроке я покажу, как конвертировать Java объект в JSON или с JSON в Java объект.
Шаг 1
Для того чтобы манипулировать конвертированием мы будем использовать библиотеку Jackson.
Давайте создадим Maven project и добавим в pom.xml следующие зависимости:
Шаг 2
Теперь добавим POJO который мы и будем конвертировать:
Обратите внимание, обязательно должен быть пустой конструктор и getter & setter.
Как вы видите мы используем две аннотации:
@JsonProperty(“name”) – эта говорит, что данный атрибут в JSON будет именоваться как name
@JsonIgnore – а эта аннотация игнорирует данный атрибут и в результате конвертирования он не будет добавлен в JSON.
Шаг 3
Теперь создадим класс конверте где будут все методы, и назовем его Converter.java:
В результате выполнения появится файл user.json:
И после конвертации обратно в POJO мы получим объект с полями:
Возникнут вопросы, выслушаю вас в комментариях.
ПОХОЖИЕ ПУБЛИКАЦИИ
16 комментариев к статье «Конвертация Java объектов в JSON»
Спасибо за статью, как раз сейчас разбираюсь с JSON. Может есть у вас в планах статья о JSON-Schema (json-schema.org)? И парсинг json’a по этой схеме и создание POJO? Было бы очень круто)
Добавил в черновики, постараюсь написать, сначала сделаю хороший екзампл.
было бы полезнее данный пример привести с методами, которые позволили бы читать json не из файла. К примеру, я работаю с бд, в которой сохраняю значения полей объекта в одном строковом поле. В таком случае мне больше подошла бы такая реализация:
public String objectToJson(Object obj) throws JsonProcessingException <
String jsonObject = mapper.writeValueAsString(obj);
return jsonObject;
>
public Object jsonToObject(String json) throws IOException <
Object obj = mapper.readValue(json, Sample.class);
return obj;
>
где Sample – класс, с которым мы работаем. Затем, при вызове метода jsonToObject нам останется только привести полученный объект к нашему типу и делать с ним все, что угодно. Точно также дело обстоит с методом objectToJson – получаем чистую строку, с которой можем делать что угодно – писать в файл, в базу, выполнять манипуляции в коде и т.д. Я к тому, что такой пример был бы более абстрактен. Но в целом все отлично, мне нравятся ваши статьи
Евгений, полностью с вами согласен, но хотелось показать на примере файла, что бы новичкам было понятней.
А что будет если не добить Json нотации перед полями, класса, который мы собираемся переводить в json. И еще.. Корректно ли будет проводится конвертация встроенного класса, например Key или Location или DATATIME? Еще хотелось бы услышать про особенности использования библиотеки gson. Спасибо за доступную форму предоставления материала.
Без аннотаций вы скорее всего получите Exception но уже не помню, надо проверить, Да со встроеными классами это тоже должно нормально работать, но опять же давно делал и не могу сказать точно .
Исключений не будет, данные в любом случае попадут в файл, просто если не указать аннотацию над полем, то имя ключа в json будет соответствовать имени поля.