Использование Jackson #
Васильев Андрей Михайлович, 2024
Версии презентации
Библиотека Jackson #
Представляет собой набор инструментов для обработки данных на JVM-платформе
- Позволяет считывать и записывать данные в JSON-формате
- Предоставляет средства для удобной работы с объектами классов
- Содержит средства для работы с другими форматами данных: BSON, CSV, Protobuf, TOML, YAML, XML и т.д.
- Является именно набором связных и бинарно совместимых инструментов, а не единым большим проектом
- Поставляется под лицензией Apache 2.0, разрешающей коммерческое использование
Полезные ссылки:
- Домашняя страница: https://github.com/FasterXML/jackson
- Набор руководств на Baeldung: https://www.baeldung.com/jackson
Потоковая обработка #
Основой для эффективной обработки JSON-документов является ядро библиотеки, которое предоставляет средства для потоковой обработки данных
- Обработка документа выполняется за один проход
- Используется минимальный объём памяти
Подключение библиотеки #
В список зависимостей приложения необходимо добавить Maven-артефакт:
"com.fasterxml.jackson.core:jackson-core:2.17.2"
Документацию по классам библиотеки можно посмотреть https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.17.2/index.html
Ключевые объекты #
JsonFactoryBuilder
— строитель фабрики ключевых объектовJsonFactory
— фабрика объектов для считывания или записиJsonGenerator
— генератор JSON, создаётся отдельно для каждого документаJsonParser
— считыватель JSON, создаётся отдельно для каждого документа
С помощью JsonFactoryBuilder можно настроить общие параметры путём включения и выключения
- JacksonReadFeature, опций по считыванию данных
- JacksonWriteFeature, опций по записи данных
Создание генератора JSON #
Фабрика JsonFactory предоставляет ряд методов для создания генератора, который сможет записать свои данные: в выходной поток, файл, объект приёма данных
Помимо целевого объекта этим методам также можно передать кодировку данных
Для создания генератора, записывающего данные на стандартный поток вывода достаточно:
val factory: JsonFactory = JsonFactoryBuilder().build()
val outputGenerator: JsonGenerator = factory.createGenerator(System.out)
outputGenerator.prettyPrinter = DefaultPrettyPrinter()
По окончании формировании JSON-документа необходимо вызвать метод close()
Потоковая запись JSON #
При формировании JSON-документа необходимо последовательно описывать состояние целевого JSON-документа, включая все его компоненты
- Символ начала массива,
writeStartArray()
- Символ окончания массива,
writeEndArray()
- Символ начала объекта,
writeStartObject()
- Символ окончания объекта,
writeStartObject()
- Название поля,
writeFieldName(name)
- Строковое значение,
writeString(value)
- Числовое значение,
writeNumber(value)
- Логическое значение,
writeBoolean(value)
- Null-значение,
writeNull()
- Удобные методы для записи пары ключ-значение,
writeNumberField(name, value)
Пример записи простого объекта #
Создадим следующий JSON-документ с потоковой записью данных
{
"sides" : [ 5, 3, 4 ],
"color" : "RED",
"cool" : true
}
with(outputGenerator) {
writeStartObject()
writeFieldName("sides")
writeStartArray()
writeNumber(5)
writeNumber(3)
writeNumber(4)
writeEndArray()
writeFieldName("color")
writeString("RED")
writeBooleanField("cool", true)
writeEndObject()
close()
}
Объектное представление #
Потоковая запись данных является самым быстрым и эффективным способом формирования JSON-документов
Однако каждый документ можно соотнести к некоторой сложной структурой данных внутри приложения, которая включает в себя множество сложных полей и их значений
Модуль jackson-databind предоставляет соответствующие средства, позволяющие решить данную задачу. Данный модуль рассчитан на использование с ЯП Java
Для поддержки данной функциональности в Kotlin на платформе JVM используйте модуль jackson-module-kotlin:
com.fasterxml.jackson.module:jackson-module-kotlin:2.17.+
Средство отображения объектов #
Для выполнения преобразования Kotlin-объекта в JSON-документ и наоборот необходимо создать компонент отображения объектов, ObjectMapper:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
val mapper = jacksonObjectMapper()
Для работы также необходимо определить класс данных Kotlin, который будет формировать данные
data class Triangle(val sideA: Double, val sideB: Double, val sideC: Double)
Создадим на основе объекта данного класса соответствующий JSON-документ:
val triangle = Triangle(3.0, 4.0, 5.0)
mapper.writeValue(System.out, triangle)
Получим следующий вывод:
{"sideA":3.0,"sideB":4.0,"sideC":5.0}
Отображение Kotlin в JSON #
Компонент отображения объектов использует рефлексию, чтобы получить список свойств объекта, их типы и значения
- Если тип данных есть одновременно и в Kotlin, и в JSON, то при выводе он будет использован (null, логический тип, число, строка)
- Сложные объекты будут преобразованы в строковое представление
- Значением ключа будет являться название свойства преобразуемого объекта
Можно использовать аннотации для настройки поведения компонента отображения
Список доступных аннотаций и их воздействие на отображение данных можно прочитать в https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/2.17.2/index.html
Также стоит отметить аннотации для поддержки классов описания дат https://github.com/FasterXML/jackson-modules-java8
Переименовывание свойств #
data class Pseudo(
@JsonProperty("Money")
val info: Int,
)
val mapper = jacksonObjectMapper()
mapper.enable(SerializationFeature.INDENT_OUTPUT)
println(mapper.writeValueAsString(Pseudo(10)))
Результат работы кода:
{
"Money" : 10
}
Какой интерфейс использовать #
- Низкоуровневый интерфейс предоставляет самый эффективный способ по преобразованию данных в рамках данного набора библиотек
- Низкоуровневый интерфейс требует написания кода, отсутствует поведение «по умолчанию»
- Компонент преобразования данных использует рефлексию, т.е. каждый раз тратится время на анализ структуры преобразуемого объекта
- Для модификации поведения необходимо разбираться в доступных аннотациях
- Аннотации могут предоставляться в отдельных библиотеках и разработаны самостоятельно
- Для написания аннотаций надо понимать структуру низкоуровневого интерфейса
- Невозможно сделать два представления для одного объекта с помощью компонента преобразования данных
- Операции преобразования из JSON в объекты Kotlin значительно сложнее при использовании низкоуровневого интерфейса