Json to list object Kotlin

Serialization is the process of converting data used by an application to a format that can be transferred over a network or stored in a database or a file. In turn, deserialization is the opposite process of reading data from an external source and converting it into a runtime object. Together they are an essential part of most applications that exchange data with third parties.

Some data serialization formats, such as JSON and protocol buffers are particularly common. Being language-neutral and platform-neutral, they enable data exchange between systems written in any modern language.

In Kotlin, data serialization tools are available in a separate component, kotlinx.serialization. It consists of two main parts: the Gradle plugin –org.jetbrains.kotlin.plugin.serialization and the runtime libraries.

kotlinx.serialization provides sets of libraries for all supported platforms – JVM, JavaScript, Native – and for various serialization formats – JSON, CBOR, protocol buffers, and others. You can find the complete list of supported serialization formats below.

All Kotlin serialization libraries belong to the org.jetbrains.kotlinx: group. Their names start with kotlinx-serialization- and have suffixes that reflect the serialization format. Examples:

  • org.jetbrains.kotlinx:kotlinx-serialization-json provides JSON serialization for Kotlin projects.

  • org.jetbrains.kotlinx:kotlinx-serialization-cbor provides CBOR serialization.

Platform-specific artifacts are handled automatically; you don't need to add them manually. Use the same dependencies in JVM, JS, Native, and multiplatform projects.

Note that the kotlinx.serialization libraries use their own versioning structure, which doesn't match Kotlin's versioning. Check out the releases on GitHub to find the latest versions.

kotlinx.serialization includes libraries for various serialization formats:

Note that all libraries except JSON serialization [kotlinx-serialization-core] are Experimental, which means their API can be changed without notice.

There are also community-maintained libraries that support more serialization formats, such as YAML or Apache Avro. For detailed information about available serialization formats, see the kotlinx.serialization documentation.

Let’s take a look at how to serialize Kotlin objects into JSON.

Before starting, you’ll need to configure your build script so that you can use Kotlin serialization tools in your project:

  1. Apply the Kotlin serialization Gradle plugin org.jetbrains.kotlin.plugin.serialization [or kotlin[“plugin.serialization”] in the Kotlin Gradle DSL].

    plugins { kotlin["jvm"] version "1.6.10" kotlin["plugin.serialization"] version "1.6.10" }

    plugins { id 'org.jetbrains.kotlin.jvm' version '1.6.10' id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10' }

  2. Add the JSON serialization library dependency:org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2

    dependencies { implementation["org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"] }

    dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2' }

Now you're ready to use the serialization API in your code. The API is located in the the kotlinx.serialization package and its format-specific subpackages such as kotlinx.serialization.json.

First, make a class serializable by annotating it with @Serializable.

import kotlinx.serialization.Serializable @Serializable data class Data[val a: Int, val b: String]

You can now serialize an instance of this class by calling Json.encodeToString[].

import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.encodeToString @Serializable data class Data[val a: Int, val b: String] fun main[] { val json = Json.encodeToString[Data[42, "str"]] }

As a result, you get a string containing the state of this object in the JSON format: {"a": 42, "b": "str"}

You can also serialize object collections, such as lists, in a single call.

val dataList = listOf[Data[42, "str"], Data[12, "test"]] val jsonList = Json.encodeToString[dataList]

To deserialize an object from JSON, use the decodeFromString[] function:

import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.decodeFromString @Serializable data class Data[val a: Int, val b: String] fun main[] { val obj = Json.decodeFromString["""{"a":42, "b": "str"}"""] }

For more information about serialization in Kotlin, see the Kotlin Serialization Guide.

Last modified: 05 March 2022

In this article we'll be taking a look at how to read and write JSON files in Kotlin, specifically, using the Jackson library.

Jackson Dependency

To utilize Jackson, we'll want to add its jackson-module-kotlin dependency to our project. If you're using Maven, you can simply add:

com.fasterxml.jackson.module jackson-module-kotlin 2.12.1

Or, if you're using Gradle, you can add:

implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1'

With the dependency in place, let's define a JSON object we'll want to read:

{ "id":101, "username":"admin", "password":"Admin123", "fullName":"Best Admin" }

Reading a JSON Object to Kotlin Object

Let's take a look at how we can deserialize a JSON object into a Kotlin object. Since we'll want to convert the JSON contents into a Kotlin object, let's define a User data class:

data class User [ val id: Int, val username: String, val password: String, val fullName: String ]

Then, we'll want to instantiate the object mapper, which can easily be done with:

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper val mapper = jacksonObjectMapper[] val jsonString = """{ "id":101, "username":"admin", "password":"Admin123", "fullName":"Best Admin" }"""

With that done, we can pass JSON contents into our ObjectMapper methods, such as readValue[], as usual for Jackson:

val userFromJson = mapper.readValue[jsonString] val userFromJsonWithType: User = mapper.readValue[jsonString]

If we print the value of userFromJson we'll see something like this:

User[id=101, username=admin, password=Admin123, fullName=Best Admin]

The readValue[] function can be used with or without the Class parameter, as you saw a little earlier with the two variables [userFromJson and userFromJsonWithType].

Note: Using the function without the Class parameter will materialize the type and automatically create a TypeReference for Jackson.

Writing a JSON Object From Kotlin Object

Now, let's take a look at how we can serialize a Kotlin object into a JSON object.

We'll use the same data class [User] and play a little with the features of Jackson:

val user = User[102, "test", "pass12", "Test User"] val userJson = mapper.writeValueAsString[user] println[userJson]

Here, we've instantiated a new instance of the User class, with a few values.

The writeValueAsString[] function is our key player here and serializes any object and its fields as a String.

Let's print the userFromJson String variable and see how it looks like:

{ "id":102, "username":"test", "password":"pass12", "fullName":"Test User" }

Writing Kotlin List to JSON Array

Oftentimes, we're dealing with lists and arrays, rather than singular objects. Let's take a look at how we can serialize a Kotlin List into a JSON array.

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Using the same data class, we'll create a list of Users and serialize them into a JSON array:

val userList = mutableListOf[] userList.add[User[102, "jsmith", "[email protected]", "John Smith"]] userList.add[User[103, "janed", "Pass1", "Jane Doe"]] val jsonArray = mapper.writeValueAsString[userList] println[jsonArray]

This results in:

[ { "id":102, "username":"jsmith", "password":"[email protected]", "fullName":"John Smith" }, { "id":103, "username":"janed", "password":"Pass1", "fullName":"Jane Doe" } ]

Reading JSON Array to Kotlin List

Now, let's go the other way around, and deserialize a JSON array into a Kotlin List:

val userListFromJson: List = mapper.readValue[jsonArray] println[userListFromJson]

And this results in:

[User[id=102, username=test, password=pass12, fullName=Test User], User[id=103, username=user, password=Pass1, fullName=Demo User]]

Handling JSON Bidirectional Relationships

Another common thing to encounter is bidirectional relationships. In a lot of cases, you might want to have a one-to-many or many-to-one relationship between some objects.

For this, let's create a data class Author which can contain one or more objects of type Book and the Book can have one Author:

data class Author [ val name: String, val books: MutableList ] data class Book [ val title: String, val author: Author ]

The Author contains a list, called books, of type Book. At the same time, the Book contains an Author instance.

Let's make a few instances and try to serialize them:

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]

If we were to do the same thing as before:

val authors = mapper.writeValueAsString[author]

We'd quickly run into an issue:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion [StackOverflowError] [through reference chain: Book["author"]->Author["books"]->...

The Author contains the Book, which contains the Author, which contains the Book, which contains the Author, and so on, until a StackOverflowError is thrown.

This is thankfully, easily fixable, using the @JsonManagedReference and @JsonBackReference annotations. We let Jackson know about this bidirectional relationship, that could go on forever:

data class Author [ val name: String, val books: MutableList ]; data class Book [ val title: String, val author: Author ];

@JsonManagedReference specifies the attribute that will be serialized normally and the @JsonBackReference will specify the attribute omitted from serialization. We'll effectively take out the author reference from the Book when each is serialized.

Now, we can serialize the authors:

val authors = mapper.writeValueAsString[author] println[authors]

If we run this code, it'll produce:

{ "name":"JK Rowling", "books":[ { "title":"Harry Potter 1" }, { "title":"Harry Potter 2" } ] }

Conclusion

In this tutorial, we've gone over how to read and write JSON Files in Kotlin with Jackson.

We've gone over the required dependencies, how to serialize and deserialize JSON and Kotlin objects, as well as arrays and lists.

Finally, we've covered how to handle bidirectional relationships when serializing and deserializing.

Video liên quan

Chủ Đề