1. Overview
Google Gson is one of the most popular libraries for handling JSON objects, and many popular programming languages support it, including Kotlin.
In this tutorial, we’ll explore how to serialize and deserialize JSON arrays with Kotlin using Gson.
2. Google Gson
In the context of Google Gson, serialization is a mechanism for converting the state of an object into a JSON representation. On the other hand, deserialization is the reverse process, where the JSON is used to recreate the actual object in memory.
Gson helps us handle these processes by offering JSON data-binding support for Kotlin, and it’s an excellent tool for managing complex data types.
2.1. Maven Dependency
First, we need to add the Gson Maven dependency to our pom.xml:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.8</version>
</dependency>
3. Implementation
For our case study, we’ll consider the example of authors and articles. One author can have multiple articles, and one article can belong to only one author. We’ll take advantage of data classes in Kotlin to implement the serialize/deserialize processes.
Let’s first create the Article and Author classes that we’ll use to parse with Gson:
data class Article(
var title: String,
var category: String,
var views: Int
) {
}
data class Author(
var name: String,
var type: String? = null,
var articles: List<Article>? = null
) {
}
3.1. Array Serialization
Gson has the built-in feature of parsing to and from an array or a list automatically into JSON objects and vice versa. For instance, we can define a list of Author objects with just the name and serialize it to a JSON Array object:
@Test
fun serializeObjectListTest() {
val authors = listOf(
Author("John", "Technical Author"),
Author("Jane", "Technical Author"),
Author("William", "Technical Editor")
)
val serialized = Gson().toJson(authors)
val json =
"""[{"name":"John","type":"Technical Author"},{"name":"Jane","type":"Technical Author"},{"name":"William","type":"Technical Editor"}]"""
assertEquals(serialized, json)
}
Gson also provides the ability to serialize arrays with nonmatching JSON keys and object field names. For example, we can serialize the articles list using a different name:
data class Author(
var name: String,
var type: String? = null,
@SerializedName("author_articles")
var articles: List<Article>? = null,
) {
}
As result, the Article array will be serialized with a different name into the resulting JSON:
@Test
fun serializeObjectListWithNonMatchingKeysTest() {
val authors = listOf(
Author(
"John",
"Technical Author",
listOf(Article("Streams in Java", "Java", 3), Article("Lambda Expressions", "Java", 5))
),
Author("Jane", "Technical Author", listOf(Article("Functional Interfaces", "Java", 2))),
Author("William", "Technical Editor")
)
val serialized = Gson().toJson(authors)
val json =
"""[{"name":"John","type":"Technical Author","author_articles":[{"title":"Streams in Java","category":"Java","views":3},{"title":"Lambda Expressions","category":"Java","views":5}]},{"name":"Jane","type":"Technical Author","author_articles":[{"title":"Functional Interfaces","category":"Java","views":2}]},{"name":"William","type":"Technical Editor"}]"""
assertEquals(serialized, json)
}
3.2. Array Deserialization
Deserializing JSON arrays with Gson requires a generic type representation for all elements in the array. Therefore, we’ll need to call the TokenType constructor and encapsulate the object that Gson will evaluate:
@Test
fun deserializeObjectListTest() {
val json =
"""[{"name":"John","type":"Technical Author"},{"name":"Jane","type":"Technical Author"},{"name":"William","type":"Technical Editor"}]"""
val typeToken = object : TypeToken<List>() {}.type
val authors = Gson().fromJson<List>(json, typeToken)
assertThat(authors).isNotEmpty
assertThat(authors).hasSize(3)
assertThat(authors).anyMatch { a -> a.name == "John" }
assertThat(authors).anyMatch { a -> a.type == "Technical Editor" }
}
It’s important to note that by default, Gson will set null values for missing fields in the JSON resulting object. Thus, in other libraries like Jackson, we need to expect that behavior even for classes with default values.
Here, our JSON string is missing the type field for all the elements in the array:
@Test
fun deserializeObjectListWithMissingFieldsTest() {
val json =
"""[{"name":"John"},{"name":"Jane"},{"name":"William"}]"""
val typeToken = object : TypeToken<List<Author>>() {}.type
val authors = Gson().fromJson<List<Author>>(json, typeToken)
assertThat(authors).isNotEmpty
assertThat(authors).hasSize(3)
assertThat(authors).anyMatch { a -> a.name == "John" }
assertThat(authors).allMatch { a -> a.type == null }
}
4. Conclusion
In this tutorial, we’ve explored how to use the Google Gson library to serialize and deserialize JSON arrays with Kotlin. We also saw how to manage the parsing of JSON arrays with missing fields and how to specify custom key names to be used for a JSON array.
As always, the complete code is available over on GitHub.