Jetpack Compose: Kotlinx serialization alternative JSON names

Introduction

In Android development, handling JSON data can be challenging, especially when JSON field names differ from the property names used in our data classes. Kotlin's kotlinx.serialization library provides an efficient way to handle such cases, making it easy to work with alternative JSON field names without compromising the structure of your code. In this example, we'll explore how Jetpack Compose and kotlinx.serialization work together to handle JSON data using alternative field names, allowing developers to specify different names for JSON properties without modifying the core data model.

The code provided demonstrates a simple Android app built with Jetpack Compose that displays user information extracted from JSON strings. It leverages the JsonNames annotation from kotlinx.serialization to assign alternative names to JSON fields, enabling flexibility in data handling. This approach is particularly useful in real-world applications where APIs may provide inconsistent field names across different responses. Let's break down the components of this code to understand how they work together to achieve seamless data handling in a Compose UI.

Main Components of the Code

The MainActivity.kt file is the heart of this example. The MainActivity class extends ComponentActivity and overrides the onCreate method to set up the app's UI using Jetpack Compose. Inside onCreate, the ComposeNetworkTheme is applied to give the app a consistent theme, and a Scaffold is used to structure the layout. The Scaffold includes a top app bar with the title "Serialization Alternative Json Names" and a main content area where user information is displayed.

The MainContent composable function is where the JSON data is deserialized and displayed. The function defines two JSON strings representing different users, each with a unique format. These strings are then decoded into User objects using Json's decodeFromString method. The Json instance is configured with isLenient = true to allow more flexible parsing, which is helpful when dealing with JSON structures that may not strictly follow JSON standards.

In the User data class, the @Serializable annotation indicates that this class is serializable. The key feature here is the use of the @JsonNames annotation on the firstName property, which allows the User object to recognize both "firstName" and "nickName" as valid field names for the firstName property. This capability enables the deserialization of JSON strings with varying field names, making the app more resilient to changes or inconsistencies in JSON responses.

Code Walkthrough

  1. Setting Up the UI with Jetpack Compose
    The MainActivity sets up the app's main layout using Jetpack Compose. A Scaffold with a top app bar is used to provide a clean and structured layout. The title "Serialization Alternative Json Names" in the top bar gives the user an idea of the app's functionality, focusing on serialization and JSON parsing.

  2. Deserializing JSON Data with kotlinx.serialization
    Inside the MainContent composable, two JSON strings (userString and userString2) are defined, each representing a different user. The Json instance, configured with isLenient = true, decodes these strings into User objects. This configuration allows for flexible parsing of JSON structures, such as those with missing quotation marks around field names.

  3. Displaying Data in Compose UI
    The user information is displayed using a Column layout, which fills the available screen space and adds padding for a clean appearance. For each User object, a Text composable displays the user's name and age. The Spacer composable adds vertical spacing between user entries, improving the readability of the information presented on the screen.

  4. Configuring Serialization
    The User data class defines the properties firstName, lastName, and age, with @Serializable applied to the class. The @JsonNames("nickName") annotation on the firstName property tells kotlinx.serialization to recognize either "firstName" or "nickName" in JSON data as valid field names for this property, allowing both JSON strings to be deserialized into the same User data structure.

  5. Dependency Setup
    To use kotlinx.serialization in the project, specific dependencies are added to the build.gradle files. The project-level build.gradle file includes the Kotlin serialization classpath, while the app-level build.gradle file applies the kotlinx-serialization plugin and includes the kotlinx-serialization-json library as a dependency.

Summary

This example demonstrates how Jetpack Compose and kotlinx.serialization can work together to handle JSON data with alternative field names in a Kotlin-based Android app. By using the @JsonNames annotation, the code allows flexible deserialization, accommodating varying JSON field names while keeping the data class structure intact. This feature is especially valuable for apps interacting with APIs that may not always provide consistent data formats.

With kotlinx.serialization, developers can achieve greater flexibility in handling JSON data, reducing the need for extensive code modifications when JSON structures change. The combination of Jetpack Compose and kotlinx.serialization provides a powerful toolkit for building Android apps with dynamic, data-driven interfaces. This approach simplifies the process of managing JSON data and enhances the app's resilience to data format variations, making it a valuable technique for modern Android development.


MainActivity.kt

package com.cfsuman.composenetwork

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.cfsuman.composenetwork.ui.theme.ComposeNetworkTheme
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNames


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeNetworkTheme {
                Scaffold(
                    topBar = {
                        TopAppBar(
                            title = {
                                Text("Serialization Alternative Json Names")
                            }
                        )
                    },
                    content = { MainContent()}
                )
            }
        }
    }
}


@Composable
fun MainContent() {
    val format = Json{ isLenient = true}

    val userString = "{ firstName: Anamika," +
            "lastName: Rahman, age: 23 }"
    val user = format.decodeFromString<User>(userString)

    val userString2 = "{ nickName: Sofia," +
            "lastName: Khatun, age:26 }"
    val user2 = format.decodeFromString<User>(userString2)

    Column(Modifier.fillMaxSize().padding(24.dp)) {
        Text(
            text = "${user.firstName} ${user.lastName}" +
                    "\nAge ${user.age}",
            style = MaterialTheme.typography.h5
        )

        Spacer(modifier = Modifier.height(12.dp))
        Text(
            text = "${user2.firstName} ${user2.lastName}" +
                    "\nAge ${user2.age}",
            style = MaterialTheme.typography.h5
        )
    }
}


@Serializable
data class User(
    @JsonNames("nickName")
    val firstName: String,
    val lastName: String,
    val age: Int
)
build.gradle [project]

buildscript {
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-serialization:1.6.10"
    }
}
build.gradle [app]

plugins {
    id 'kotlinx-serialization'
}


dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0'
}
More android jetpack compose tutorials