1. Введение
Нотация объектов JavaScript, или JSON, приобрела большую популярность в качестве формата обмена данными в последние годы. Jsoniter — это новая библиотека синтаксического анализа JSON, призванная предложить более гибкий и более производительный синтаксический анализ JSON, чем другие доступные синтаксические анализаторы.
В этом руководстве мы увидим, как анализировать объекты JSON с помощью библиотеки Jsoniter для Java.
2. Зависимости
Последнюю версию Jsoniter можно найти в репозитории Maven Central.
Начнем с добавления зависимостей в pom.xml
:
<dependency>
<groupId>com.jsoniter<groupId>
<artifactId>jsoniter</artifactId>
<version>0.9.23</version>
</dependency>
Точно так же мы можем добавить зависимость в наш файл build.gradle
:
compile group: 'com.jsoniter', name: 'jsoniter', version: '0.9.23'
3. Разбор JSON с использованием Jsoniter
Jsoniter предоставляет 3 API для анализа документов JSON:
- Привязать API
- Любой API
- Итератор API
Давайте рассмотрим каждый из вышеперечисленных API.
3.1. Разбор JSON с помощью Bind API
API привязки использует традиционный способ привязки документа JSON к классам Java.
Рассмотрим документ JSON со сведениями о студенте:
{"id":1,"name":{"firstName":"Joe","surname":"Blogg"}}
Давайте теперь определим классы схемы Student
и Name
для представления вышеуказанного JSON:
public class Student {
private int id;
private Name name;
// standard setters and getters
}
public class Name {
private String firstName;
private String surname;
// standard setters and getters
}
Десериализация JSON в объект Java с помощью API привязки очень проста. Мы используем метод десериализации
JsonIterator
:
@Test
public void whenParsedUsingBindAPI_thenConvertedToJavaObjectCorrectly() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Student student = JsonIterator.deserialize(input, Student.class);
assertThat(student.getId()).isEqualTo(1);
assertThat(student.getName().getFirstName()).isEqualTo("Joe");
assertThat(student.getName().getSurname()).isEqualTo("Blogg");
}
Класс схемы Student
объявляет идентификатор
типа данных int
.
Однако что, если JSON, который мы получаем, содержит значение String
для идентификатора
вместо числа? Например:
{"id":"1","name":{"firstName":"Joe","surname":"Blogg"}}
Обратите внимание, что на этот раз идентификатор
в JSON представляет собой строковое значение «1»
. Jsoniter предоставляет декодеры Maybe
для работы с этим сценарием.
3.2. Возможно
Декодеры
Декодеры Maybe
от Jsoniter пригодятся, когда тип данных элемента JSON нечеткий . Тип данных для поля student.id
нечеткий — это может быть либо String
, либо int
. Чтобы справиться с этим, нам нужно аннотировать поле id
в нашем классе схемы с помощью MaybeStringIntDecoder
:
public class Student {
@JsonProperty(decoder = MaybeStringIntDecoder.class)
private int id;
private Name name;
// standard setters and getters
}
Теперь мы можем анализировать JSON, даже если значение id
является строкой
:
@Test
public void givenTypeInJsonFuzzy_whenFieldIsMaybeDecoded_thenFieldParsedCorrectly() {
String input = "{\"id\":\"1\",\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Student student = JsonIterator.deserialize(input, Student.class);
assertThat(student.getId()).isEqualTo(1);
}
Точно так же Jsoniter предлагает другие декодеры, такие как MaybeStringLongDecoder
и MaybeEmptyArrayDecoder
.
Теперь давайте представим, что мы ожидали получить документ JSON с данными о студенте
, но вместо этого получили следующий документ:
{"error":404,"description":"Student record not found"}
Что здесь случилось? Мы ожидали успешного ответа с данными о студентах
, но получили ответ об ошибке .
Это очень распространенный сценарий, но как бы мы справились с этим?
Один из способов — выполнить нулевую
проверку, чтобы увидеть, получили ли мы ответ об ошибке перед извлечением данных Student
. Однако нулевые
проверки могут привести к сложному для чтения коду, и проблема усугубляется, если у нас есть многоуровневый вложенный JSON.
На помощь приходит парсинг Jsoniter с помощью Any
API.
3.3. Разбор JSON с использованием Any API
Когда сама структура JSON является динамической, мы можем использовать Any
API Jsoniter, который обеспечивает синтаксический анализ JSON без схемы . Это работает аналогично синтаксическому анализу JSON в Map<String, Object>
.
Давайте проанализируем Student
JSON, как и раньше, но на этот раз с помощью Any
API:
@Test
public void whenParsedUsingAnyAPI_thenFieldValueCanBeExtractedUsingTheFieldName() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Any any = JsonIterator.deserialize(input);
assertThat(any.toInt("id")).isEqualTo(1);
assertThat(any.toString("name", "firstName")).isEqualTo("Joe");
assertThat(any.toString("name", "surname")).isEqualTo("Blogg");
}
Давайте разберемся в этом примере. Во-первых, мы используем JsonIterator.deserialize(..)
для разбора JSON. Однако в этом случае мы не указываем класс схемы. Результат имеет тип Any.
Затем мы читаем значения полей, используя имена полей. Читаем значение поля «id» с помощью метода Any.toInt
. Метод toInt
преобразует значение «id» в целое число. Точно так же мы читаем значения полей «name.firstName» и «name.surname» как строковые значения, используя метод toString
.
Используя Any
API, мы также можем проверить, присутствует ли элемент в JSON. Мы можем сделать это, найдя элемент, а затем проверив valueType
результата поиска. Тип значения
будет INVALID
, если элемент отсутствует в JSON.
Например:
@Test
public void whenParsedUsingAnyAPI_thenFieldValueTypeIsCorrect() {
String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
Any any = JsonIterator.deserialize(input);
assertThat(any.get("id").valueType()).isEqualTo(ValueType.NUMBER);
assertThat(any.get("name").valueType()).isEqualTo(ValueType.OBJECT);
assertThat(any.get("error").valueType()).isEqualTo(ValueType.INVALID);
}
Поля «id» и «name» присутствуют в JSON, и, следовательно, их тип значения
— NUMBER
и OBJECT
соответственно. Однако во входных данных JSON нет элемента с именем «ошибка», поэтому тип значения
имеет значение INVALID
.
Возвращаясь к сценарию, упомянутому в конце предыдущего раздела, нам нужно определить, является ли полученный нами ввод JSON успешным или ошибочным. Мы можем проверить, получили ли мы ответ об ошибке, проверив значение
типа элемента «ошибка»:
String input = "{\"error\":404,\"description\":\"Student record not found\"}";
Any response = JsonIterator.deserialize(input);
if (response.get("error").valueType() != ValueType.INVALID) {
return "Error!! Error code is " + response.toInt("error");
}
return "Success!! Student id is " + response.toInt("id");
При запуске приведенный выше код вернет «Ошибка!! Код ошибки 404»
.
Далее мы рассмотрим использование Iterator API для анализа документов JSON.
3.4. Разбор JSON с использованием API итератора
Если мы хотим выполнить привязку вручную, мы можем использовать Jsoniter Iterator
API. Рассмотрим JSON:
{"firstName":"Joe","surname":"Blogg"}
Мы будем использовать класс схемы Name
, который мы использовали ранее для анализа JSON с помощью API Iterator :
@Test
public void whenParsedUsingIteratorAPI_thenFieldValuesExtractedCorrectly() throws Exception {
Name name = new Name();
String input = "{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}";
JsonIterator iterator = JsonIterator.parse(input);
for (String field = iterator.readObject(); field != null; field = iterator.readObject()) {
switch (field) {
case "firstName":
if (iterator.whatIsNext() == ValueType.STRING) {
name.setFirstName(iterator.readString());
}
continue;
case "surname":
if (iterator.whatIsNext() == ValueType.STRING) {
name.setSurname(iterator.readString());
}
continue;
default:
iterator.skip();
}
}
assertThat(name.getFirstName()).isEqualTo("Joe");
assertThat(name.getSurname()).isEqualTo("Blogg");
}
Давайте разберемся в приведенном выше примере. Сначала мы анализируем
документ JSON как итератор. Мы используем полученный экземпляр JsonIterator
для перебора элементов JSON:
- Мы начинаем с вызова метода
readObject
, который возвращает имя следующего поля (илиnull
, если достигнут конец документа). - Если имя поля нас не интересует, мы пропускаем элемент JSON с помощью метода
skip
. В противном случае мы проверяем тип данных элемента с помощью методаwhatIsNext
. Вызов методаwhatIsNext
не является обязательным, но полезен, когда тип данных поля нам неизвестен. - Наконец, мы извлекаем значение элемента JSON, используя метод
readString
.
4. Вывод
В этой статье мы обсудили различные подходы, предлагаемые Jsoniter для разбора документов JSON как объектов Java.
Во-первых, мы рассмотрели стандартный способ анализа документа JSON с использованием класса схемы.
Далее мы рассмотрели обработку нечетких типов данных и динамических структур при анализе документов JSON с использованием декодеров Maybe и
любого
типа данных соответственно.
Наконец, мы рассмотрели API-интерфейс Iterator
для ручной привязки JSON к объекту Java.
Как всегда, исходный код примеров, используемых в этой статье, доступен на GitHub .