Перейти к основному содержимому

Введение в Jsoniter

· 6 мин. чтения

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:

  1. Мы начинаем с вызова метода readObject , который возвращает имя следующего поля (или null , если достигнут конец документа).
  2. Если имя поля нас не интересует, мы пропускаем элемент JSON с помощью метода skip . В противном случае мы проверяем тип данных элемента с помощью метода whatIsNext . Вызов метода whatIsNext не является обязательным, но полезен, когда тип данных поля нам неизвестен.
  3. Наконец, мы извлекаем значение элемента JSON, используя метод readString .

4. Вывод

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

Во-первых, мы рассмотрели стандартный способ анализа документа JSON с использованием класса схемы.

Далее мы рассмотрели обработку нечетких типов данных и динамических структур при анализе документов JSON с использованием декодеров Maybe и любого типа данных соответственно.

Наконец, мы рассмотрели API-интерфейс Iterator для ручной привязки JSON к объекту Java.

Как всегда, исходный код примеров, используемых в этой статье, доступен на GitHub .