Как создать файл json на андроид

Обновлено: 06.07.2024

Библиотека GSON была разработана программистами Google и позволяет конвертировать объекты JSON в Java-объекты и наоборот.

Установим зависимость в Gradle.

Конвертируем объект в JSON

Создадим простейший класс Cat с открытыми полями.

Попробуем сконвертировать объект созданного класса в JSON при помощи метода toJson().

В логах видим строку:

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

Перепишем пример немного иначе.

Смотрим на ответ. Теперь все данные данные выводятся по алфавиту.

Конвертируем JSON в объект

Естественно, нам нужно уметь выполнять и обратную задачу. Допустим с сервера пришёл ответ в виде JSON-строки и мы должны из неё построить объект для работы в своём приложении. В этом случае вызывается метод fromJson().

В этом примере нам пришлось экранировать кавычки. Но суть от этого не меняется. Получив строку, мы смогли создать объект murzik и узнать его имя, возраст, цвет.

Чтобы избежать надоедливых экранирований кавычек, можно заменить их на одиночные кавычики.

Сложный класс

Класс Cat состоит из примитивных типов. Но иногда классы содержат объекты других классов. Усложним класс, добавив новый класс Address.

Посмотрим, что получится.

GSON справился с заданием и показал правильный результат (опять по алфавиту).

Пробуем в обратном порядке - из json-строки получим объект.

Аннотации

Можно использовать аннотации, чтобы помочь библиотеке разобраться с полями класса, если они не совпадают с нужным именем в json.

Мы не рассмотрели примеры, когда объект содержит массив/список элементов, отображения (Map), Set.

Пример для Kotlin

На данный момент использовать библиотеку GSON в Kotlin не рекомендуется, так как она не учитывает возможности языка при работе с null. Используйте альтернативы в виде библиотек Moshi, Jackson или специального плагина kotlinx.serialization.

Тем не менее приведу один пример. Для простоты создадим строку самостоятельно в формате JSON.

image

В одной из моих прошлых статей я рассказывал о своем opensorce pet проекте useful-java-links, идея которого собрать как можно больше ссылок на полезные Java библиотеки и фреймворки. У него так же есть подпроект Hello World project идея которого для каждой библиотеки собрать несколько простых примеров её использования.

Проблема программистов в Java мире в том что кроме стандартной библиотеки JDK есть огромное других полезных библиотек, причем переход от одной библиотеки к другой может вызывать проблемы из-за неполной документации, отсутствия простых примеров или даже сложности понять какие зависимости нужно добавить в maven чтобы все запустилось. А на новой работе вполне могут использовать вместо твоей любимой библиотеки ту которую ты не знаешь. Идея моего проекта облегчить изучение и выбор разных библиотек.

Итак, давайте посмотрим какие известные библиотеки есть для работы с JSON в Java…

8. Работа с Json

JSON парсеры
    Быстрый JSON обработчик, рейтинг github'а — 4851. User guide и Hello World examples. Лицензия: Apache 2. — Простая сериализации объектов в JSON и обратно. Хорошая производительность и легкость в использовании, рейтинг github'а — 4120. User guide и Hello World examples. Лицензия: Apache 2. -Библиотека парсинга и сериализации JSON, основанная на Jackson's streaming API. По словам разработчиков, превосходит по производительности GSON и Jackson библиотеки, рейтинг github'а — 2188. User guide и Hello World examples. Лицензия: Apache 2. Реализация работы с JSON в Java от разработчиков JSON стандарта, рейтинг github'а — 1937. User guide и Hello World examples. Лицензия: Crockford's license (MIT License + "Good, not Evil"). JSON библиотека для Android и Java, служит для упрощения парсинга Json в объекты Java, рейтинг github'а — 1732. User guide и Hello World examples. Лицензия: Apache 2 Быстрый JSON парсер для java проектов, рейтинг github'а — 921. User guide и Hello World examples. Лицензия: BSD 3. — Похоже на GSON, но более производительна, если вам нужно часто создавать экземпляр библиотеки. Подпроекты: Jackson core Базовая часть функционала, Jackson databind Базовая реализация databind'а, рейтинг github'а — 881. User guide и Hello World examples. Лицензия: Apache 2. — Мощная и простая в использовании Java библиотека для преобразования в/из JSON, рейтинг github'а — 108. User guide и Hello World examples. Лицензия: Apache 2.
Аналог XPath для JSON
Генерация Java классов из JSON или JSON схемы и JSON валидация
    Генерация Java классов из JSON схемы (или примера JSON) с аннотациями для data-binding для Jackson 1.x or 2.x, Gson и т. п. рейтинг github'а — 1664. User guide и Hello World examples. Лицензия: Apache 2. Валидация JSON схемы, реализована на чистой Java, создана с целью проверки Json файлов, используя Json схемы., так же может генерировать Java классы из схемы и наоборот, рейтинг github'а — 547. User guide и Hello World examples. Лицензия: GNU Lesser 3/Apache 2.

Итак, у нас восемь библиотек для сериализации и десериализации в json, две библиотеки для генерации Java классов по схеме или json файлу, одна библиотека для валидации схемы и два аналога XPath, но для json. Давайте рассмотрим каждую из них.

1. JSON парсеры

Существует три основных способа сериализации и десериализации среди указанных библиотек (от самого простого к самому сложному) и один дополнительный:

  1. Data bind,
  2. Tree Model,
  3. Streaming API,
  4. (И дополнительный способ) Аналоги 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. Зато никаких ограничений по памяти и в теории максимальная производительность.
Плюсы: производительность и минимальное потребление памяти,
Минусы: сложность использования,

Аналоги XPath — дополнительный способ, не очень подходит, если нужно получит всю информацию из json'a, зато позволяет написав выражение $.store.book[*].author и получить список всех авторов всех книг из json'a магазина. То есть легко получать часть информации из json'а.
Плюсы: позволяет быстро получить информацию из 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 классы (в разных примерах могут слегка отличаться наличием аннотаций, если они обязательны):

Примеры использования (Data bind):
Способ
Fastjson Gson LoganSquare Moshi Ig json parser Jackson Genson
Инициализация --- Gson gson = new Gson() --- Moshi moshi = new Moshi.
Builder().build(); JsonAdapter<Human>
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.

Обозначение объектов JavaScript или, короче говоря, JSON -это формат обмена данными, который был введен в 1999 году и получил широкое распространение в середине 2000-х годов. В настоящее время это де-факто стандартный формат для общения между веб-сервисами и их клиентами (браузерами, мобильными приложениями и т.д.). Знание того, как читать и писать, является необходимым навыком для любого разработчика программного обеспечения.

Несмотря на то, что ДЖЕЙСОН был получен из JavaScript, это независимый от платформы формат. Вы можете работать с ним на нескольких языках программирования, включая Java, Python, Ruby и многие другие. Действительно, любой язык, который может анализировать строку, может обрабатывать JSON.

Популярность JSON привела к его встроенной поддержке многими базами данных, последние версии PostgreSQL и MySQL содержат встроенную поддержку запроса данных, хранящихся в полях JSON. Базы данных NoSQL, такие как MongoDB , были построены на основе этого формата и используют документы JSON для хранения записей, точно так же, как таблицы и строки хранят записи в реляционной базе данных.

Одним из основных преимуществ JSON по сравнению с форматом данных XML является размер документа. Поскольку JSON не имеет схем, нет необходимости переносить огромные структурные накладные расходы, такие как пространства имен и оболочки.

JSON-это универсальный формат данных, который имеет шесть типов данных:

  • Строки
  • Числа
  • Логический
  • Массивы
  • Объекты
  • нулевой

Давайте взглянем на простой документ JSON:

json-просто

Поскольку в Java нет встроенной поддержки JSON, прежде всего, мы должны добавить новую зависимость, которая обеспечивала бы ее для нас. Для начала мы будем использовать модуль json-simple , добавив его в качестве зависимости Maven.

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

Давайте создадим простой метод, который будет принимать имя файла в качестве параметра и записывать некоторые жестко закодированные данные JSON:

Здесь мы создаем экземпляр класса JSONObject , указывая имя и возраст в качестве свойств. Затем мы создаем экземпляр класса JSONArray , добавляя два строковых элемента и помещая его в качестве третьего свойства нашего sampleObject . В конечном счете, мы преобразуем образец объекта в документ JSON, вызывающий метод toJSONString() и записываем его в файл.

Чтобы запустить этот код, мы должны создать точку входа в наше приложение, которая могла бы выглядеть следующим образом:

В результате выполнения этого кода мы получим файл с именем example.json в корне нашего пакета. Содержимое файла будет документом JSON со всеми свойствами, которые мы ввели:

Отлично! У нас только что был наш первый опыт работы с форматом JSON, и мы успешно сериализовали в него объект Java и записали его в файл.

Теперь, с небольшим изменением нашего исходного кода, мы можем прочитать объект JSON из файла и распечатать его на консоли либо полностью, либо распечатать выбранные отдельные свойства:

Важно отметить, что метод parse() возвращает Объект , и мы должны явно привести его к JSONObject .

Если у вас неправильный или поврежденный документ JSON, вы получите исключение, подобное этому:

Чтобы смоделировать это, попробуйте удалить последнюю закрывающую скобку > .

Копать Глубже

Несмотря на то, что json-простой полезен, он не позволяет нам использовать пользовательские классы без написания дополнительного кода. Давайте предположим, что у нас есть класс, представляющий человека из нашего первоначального примера:

Нашей задачей было бы десериализовать этот объект из файла в экземпляр класса Person . Давайте сначала попробуем сделать это с помощью simple-json .

Изменив наш метод main () , повторно используя статический readSimpleJsonDemo() и добавив необходимый импорт, мы доберемся до:

Это выглядит не очень хорошо, у нас много странных типов, но, похоже, это делает свою работу, верно?

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Давайте попробуем распечатать на консоли массив дети нашего Человека , а затем возраст первого ребенка.

Как мы видим, первый вывод консоли показывает, казалось бы, хороший результат:

но второй выдает Исключение :

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

Джексон

Библиотека, которая позволит нам делать все это очень эффективно, называется Джексон . Это очень распространено и используется в крупных корпоративных проектах, таких как Спящий режим .

Давайте добавим его в качестве новой зависимости Maven:

Основной класс , который мы будем использовать, называется ObjectMapper , у него есть метод readValue () , который принимает два аргумента: источник для чтения и класс для приведения результата.

ObjectMapper может быть настроен с несколькими различными параметрами, переданными в конструктор:

FAIL_ON_SELF_РЕФЕРЕНЦИИ Функция, которая определяет, что происходит, когда прямая самоссылка обнаруживается POJO (и для нее не включена обработка идентификатора объекта): либо создается исключение JsonMappingException (если это правда), либо ссылка обычно обрабатывается (ложь).
ОТСТУП_ВЫХОД Функция, которая позволяет включать (или отключать) отступы для базового генератора, используя принтер pretty по умолчанию, настроенный для ObjectMapper (и авторов объектов, созданных из mapper).
ORDER_MAP_ENTRIES_BY_KEYES Функция, определяющая, будут ли записи карты сначала отсортированы по ключу перед сериализацией или нет: если включено, при необходимости выполняется дополнительный шаг сортировки (не требуется для отсортированных карт), если отключено, дополнительная сортировка не требуется.
USE_EQUALITY_FOR_OBJECT_ID Функция, определяющая, сравнивается ли идентичность объекта с использованием истинной идентичности объекта на уровне JVM (ложь); или метод equals ().
Функция, определяющая, как сериализуется тип char []: при включении сериализуется как явный массив JSON (с односимвольными строками в качестве значений); при отключении по умолчанию сериализуется как строки (что более компактно).
МЕТКИ ВРЕМЕНИ WRITE_DATE_KEYS_AS_TIMESTAMPS Функция, определяющая, будут ли даты (и подтипы), используемые в качестве ключей карты, сериализованы как метки времени или нет (если нет, будут сериализованы как текстовые значения).
WRITE_DATE_TIMESTAMPS_АС_НАНОСЕКУНДЫ Функция, которая определяет, следует ли записывать числовые значения временных меток с использованием наносекундных временных меток (включено) или нет (отключено); если и только если тип данных поддерживает такое разрешение.
МЕТКИ ВРЕМЕНИ WRITE_DATES_AS_TIMESTAMPS Функция, которая определяет, следует ли сериализовать значения даты (и даты/времени) (и такие вещи, основанные на данных, как календари), как числовые метки времени (true; по умолчанию) или как что-то другое (обычно текстовое представление).
WRITE_DATES_С_ЗОНОМ_ИД Функция, которая определяет, следует ли сериализовать значения даты/времени так, чтобы они включали идентификатор часового пояса, в тех случаях, когда сам тип содержит информацию о часовом поясе.

Полный список сериализации перечисления доступен здесь .

К сожалению, после запуска этого фрагмента кода мы получим исключение:

Судя по всему, мы должны добавить конструктор по умолчанию в класс Person :

При повторном запуске кода мы увидим еще одно исключение:

Проблема здесь связана с внутренней структурой библиотеки Джексона. Он выводит имена свойств из геттеров, удаляя их первые части. В случае getafe() и getName() это работает отлично, но с женат() это не так, и предполагается, что поле должно называться женат вместо Женат .

Больше никаких исключений не появляется, и мы видим желаемый результат!

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

Мы можем достичь того же результата, добавив аннотацию к методу женат() :

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

Вывод

JSON-это легкий текстовый формат, который позволяет нам представлять объекты и передавать их через Интернет или хранить в базе данных.

В Java отсутствует встроенная поддержка манипуляций с JSON, однако существует несколько модулей, обеспечивающих эту функциональность. В этом уроке мы рассмотрели модули json-simple и Jackson , показав сильные и слабые стороны каждого из них.

Работая с JSON, вы должны иметь в виду нюансы модулей, с которыми вы работаете, и тщательно отлаживать исключения, которые могут появляться.

В этом уроке мы с вами более подробно познакомимся с форматом JSON . А потом из этого формата получим наш User объект.

JSON стал общепринятым форматом для обмена данными в клиент-серверных приложения. Он является универсальным форматом для обмена данными.

Представьте, что вам нужно создать систему приложений, в которую входит, сервер, web -клиент, ios -клиент, android -клиент. У всех технологий разные языки,
разные особенности. Поэтому сервер отвечает в формате JSON , а клиенты на своей стороне приводят к нужному формату ответы. Подробнее про JSON формат можно
почитать здесь, но на данном этапе ваших знаний уже вполне хватит.

Давайте создадим новый класс в пакете network и назовём его JsonParser для преобразования Json -строк в необходимые нам объекты. Добавим в него один
метод, который назовём getUser(String response) :

Видим, что наш JSON полностью повторяет структуру нашего объекта User . Теперь можно приступить к парсингу (преобразованию) данных.

Видим, что AndroidStudio подчёркивает эту строку, указывая что надо обработать исключение или добавить его к сигнатуре метода. Можем нажать alt + enter
и увидеть два этих варианта решения проблемы. Добавим исключение к сигнатуре, чтобы обработать исключения в клиентском коде. Для этого выберем вариант Add exception to method signature :

ExceptionSignature.jpg

Т.е. вот так по названию поля мы можем достать его значение. Нам попался самый простой случай, где наши значения являются примитивными типами. На практике мы
поработаем с массивами элементов и вложенными объектами.

Давайте создадим объект User , передав ему в конструктор все поля, которые мы только что достали из userJson :

После этого добавили строку User user = jsonParser.getUser(response); в конец метода getUserInfo и поменяли возвращаемый тип на User и добавили новое
исключение JSONException в сигнатуру метода. Отлично, теперь изменим код в UserInfoActivity и запустим приложение.

в UserInfoActivity нам нужно изменить метод loadUserInfo :

Возвращаемый тип Asynctask со String на User .

Добавили в catch блок перехват исключения типа IOException | JSONException .

В onPostExecute мы заменили вызов Log.d() на вызов реального метода отображения displayUserInfo(user) .

Давайте запустим наше приложение:

Result.jpg

Всё работает! Только видим, что наш ник выводится без символа @ . Давайте изменим метод getNick() в классе User . Будем добавлять в него символ @ самостоятельно, т.к. сервер этого не сделал.

Запустим наше приложение и увидим, что теперь с форматом поля nick всё в порядке:

Читайте также: