В этой статье мы рассмотрим, как читать и записывать файлы JSON в Kotlin , в частности, с помощью библиотеки Джексона.
Джексонская зависимость
Чтобы использовать Jackson, мы хотим добавить в наш проект
jackson-module-kotlin
Если вы используете Maven, вы можете просто
добавить:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.1</version>
</dependency>
Или, если вы используете Gradle, вы можете добавить:
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1'
Имея зависимость, давайте определим объект JSON, который мы хотим прочитать:
{
"id":101,
"username":"admin",
"password":"Admin123",
"fullName":"Best Admin"
}
Чтение объекта JSON в объект Kotlin
Давайте посмотрим, как мы можем десериализовать объект JSON в объект
Kotlin. Поскольку мы хотим преобразовать содержимое JSON в объект
Kotlin, давайте определим класс данных User
data class User (
val id: Int,
val username: String,
val password: String,
val fullName: String
)
Затем мы хотим создать экземпляр объекта сопоставления, что можно легко сделать с помощью:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
// Registering the Kotlin module with the ObjectMpper instance
val mapper = jacksonObjectMapper()
// JSON String
val jsonString = """{
"id":101,
"username":"admin",
"password":"Admin123",
"fullName":"Best Admin"
}"""
Сделав это, мы можем передать содержимое JSON в наши ObjectMapper
методы, такие как readValue()
, как обычно для Джексона:
// Read data from a JSON string
val userFromJson = mapper.readValue<User>(jsonString)
// Or
val userFromJsonWithType: User = mapper.readValue(jsonString)
Если мы напечатаем значение userFromJson
мы увидим что-то вроде этого:
User(id=101, username=admin, password=Admin123, fullName=Best Admin)
readValue()
можно использовать с Class
или без него, как вы видели
немного ранее с двумя переменными ( userFromJson
и
userFromJsonWithType
).
Примечание. Использование функции без Class
материализует тип и
автоматически создает TypeReference
для Jackson.
Написание объекта JSON из объекта Kotlin
Теперь давайте посмотрим, как мы можем сериализовать объект Kotlin в объект JSON.
Мы будем использовать тот же класс данных ( User
) и немного поиграем
с функциями Jackson:
val user = User(102, "test", "pass12", "Test User")
val userJson = mapper.writeValueAsString(user)
println(userJson)
Здесь мы создали новый экземпляр User
с несколькими значениями.
Функция writeValueAsString()
является нашим ключевым игроком здесь и
сериализует любой объект и его поля в виде строки.
userFromJson
переменную userFromJson и посмотрим, как она выглядит:
{
"id":102,
"username":"test",
"password":"pass12",
"fullName":"Test User"
}
Запись списка Kotlin в массив JSON
Часто мы имеем дело со списками и массивами, а не с отдельными объектами. Давайте посмотрим, как мы можем сериализовать список Kotlin в массив JSON .
Используя тот же класс данных, мы создадим список User
и сериализуем
их в массив JSON:
val userList = mutableListOf<User>()
userList.add(User(102, "jsmith", " [email protected] ", "John Smith"))
userList.add(User(103, "janed", "Pass1", "Jane Doe"))
val jsonArray = mapper.writeValueAsString(userList)
println(jsonArray)
Это приводит к:
[
{
"id":102,
"username":"jsmith",
"password":" [email protected] ",
"fullName":"John Smith"
},
{
"id":103,
"username":"janed",
"password":"Pass1",
"fullName":"Jane Doe"
}
]
Чтение массива JSON в список Kotlin
Теперь давайте пойдем другим путем и десериализуем массив JSON в список Kotlin :
val userListFromJson: List<User> = mapper.readValue(jsonArray)
println(userListFromJson)
И это приводит к:
[User(id=102, username=test, password=pass12, fullName=Test User), User(id=103, username=user, password=Pass1, fullName=Demo User)]
Обработка двунаправленных отношений JSON
Еще одна распространенная вещь, с которой можно столкнуться, - это двунаправленные отношения. Во многих случаях вы можете захотеть установить отношения «один ко многим» или «многие к одному» между некоторыми объектами.
Для этого давайте создадим класс данных Author
который может содержать
один или несколько объектов типа Book
а у Book
может быть один
Author
:
data class Author (
val name: String,
val books: MutableList<Book>
)
data class Book (
val title: String,
val author: Author
)
Author
содержит список, называемый books
, типа Book
. В то же
время Book
содержит экземпляр Author
Сделаем несколько экземпляров и попробуем их сериализовать:
val author = Author("JK Rowling", mutableListOf())
val bookOne = Book("Harry Potter 1", author)
val bookTwo = Book("Harry Potter 2", author)
author.books.add(bookOne)
author.books.add(bookTwo)
Если бы мы сделали то же самое, что и раньше:
val authors = mapper.writeValueAsString(author)
Мы быстро столкнулись с проблемой:
com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: Book["author"]->Author["books"]->...
Author
содержит Book
, которая содержит Author
, которая содержит
Book
, которая содержит Author
, и так далее, пока не будет
StackOverflowError
.
К счастью, это легко @JsonManagedReference
с @JsonBackReference
аннотаций @JsonManagedReference и @JsonBackReference. Мы сообщаем
Джексону об этих двунаправленных отношениях, которые могут продолжаться
вечно:
data class Author (
val name: String,
@JsonManagedReference
val books: MutableList<Book>
);
data class Book (
val title: String,
@JsonBackReference
val author: Author
);
@JsonManagedReference
указывает атрибут, который будет сериализован
нормально, а @JsonBackReference
будет указывать атрибут, опущенный при
сериализации. Мы эффективно удалим ссылку author
Book
при
сериализации каждого из них.
Теперь мы можем сериализовать авторов:
val authors = mapper.writeValueAsString(author)
println(authors)
Если мы запустим этот код, он выдаст:
{
"name":"JK Rowling",
"books":[
{
"title":"Harry Potter 1"
},
{
"title":"Harry Potter 2"
}
]
}
Заключение
В этом руководстве мы рассмотрели, как читать и писать файлы JSON в Kotlin с помощью Jackson .
Мы рассмотрели необходимые зависимости, способы сериализации и десериализации объектов JSON и Kotlin, а также массивов и списков.
Наконец, мы рассмотрели, как обрабатывать двунаправленные отношения при сериализации и десериализации.