Jetpack Compose: Kotlinx serialization handle null values

Introduction

Handling nullable values in JSON data is a common challenge in Android development, especially when using Kotlin's type-safe, expressive language features. Jetpack Compose and Kotlinx serialization libraries make this easier by allowing us to create flexible UI and serialization logic. This article will guide you through using Jetpack Compose and Kotlinx serialization in Kotlin to handle nullable JSON fields effectively. With Kotlinx's configuration options, we can manage missing or null fields smoothly, improving the robustness of our Android applications.

This tutorial example demonstrates a basic implementation where we deserialize JSON data that may contain null values. We use Jetpack Compose to display user information in a UI, and Kotlinx serialization to manage nullable JSON fields with a configuration that accommodates these potential null values. The example showcases how to use the coerceInputValues option in Kotlinx serialization to handle null fields gracefully by providing default values, ensuring a seamless user experience even with incomplete data.

Activity Setup and Jetpack Compose Scaffold

The MainActivity class extends ComponentActivity, which is the entry point of this Compose-based Android app. In onCreate, the setContent function is used to set up the UI using a custom theme called ComposeNetworkTheme. Inside this UI, a Scaffold is provided, which includes a TopAppBar and the MainContent composable as the content of the main screen. This layout provides structure to the app, with TopAppBar displaying a title ("Serialization Handle Null Values") that indicates the app's purpose.

The Scaffold component makes the UI structure reusable and manageable by defining areas for different elements, like the TopAppBar and the MainContent composable. MainContent, where the JSON handling logic resides, is the core of this example and demonstrates how data is parsed and presented to users.

Setting Up JSON Handling with Kotlinx Serialization

In the MainContent composable function, the Kotlinx serialization library is used to deserialize JSON strings. A Json instance is created with specific configurations, including isLenient = true and coerceInputValues = true. Setting isLenient = true allows Kotlinx to handle non-standard JSON inputs flexibly, such as those with missing fields or formatting quirks. Meanwhile, coerceInputValues = true is particularly helpful for handling null values; it automatically provides default values where null or missing values are encountered in the JSON data, allowing the app to continue functioning without error.

Two JSON strings are defined in MainContent. The first JSON string (userString) contains a null value for the lastName field, while the second JSON string (userString2) provides complete information. These strings simulate incoming data from a source that might have incomplete records. The decodeFromString function deserializes each JSON string into a User object, leveraging the User data class structure to handle nulls gracefully.

Using Default Values in Data Classes

The User data class is defined with @Serializable annotation, making it compatible with Kotlinx serialization. The class has three properties: firstName, lastName, and age. Notably, lastName has a default value of "Default last name", ensuring that if the field is missing or null in the JSON data, this default will be used. This approach minimizes potential NullPointerExceptions, as the app will have meaningful data to display in case of missing fields.

This default behavior, combined with the coerceInputValues setting, creates a seamless experience where the app can handle and display partially complete data without crashing or displaying unexpected behavior. In scenarios where JSON data is unreliable or incomplete, this method enhances the app's robustness.

Displaying Data with Jetpack Compose

Inside the MainContent function, a Column composable is used to layout UI components vertically. Two Text composables are created, each displaying user information based on the deserialized JSON data. By combining the firstName, lastName, and age properties, we generate text that displays each user’s full name and age. A Spacer is also used to add some vertical padding between the user details, making the UI more visually appealing.

Jetpack Compose simplifies UI creation, allowing you to directly use data class properties like user.firstName or user2.age in a declarative manner. This approach makes the code readable and ensures that the displayed data remains in sync with any changes in the underlying data model.

Summary

This example demonstrates a practical way to handle nullable JSON fields in an Android app using Jetpack Compose and Kotlinx serialization. By using coerceInputValues and setting default values in the User data class, we ensure that incomplete JSON data does not disrupt the app's functionality. Jetpack Compose's declarative syntax allows us to display data effectively and concisely, creating a user-friendly interface.

With Kotlinx serialization's flexibility and Jetpack Compose's composability, handling null values and incomplete data in JSON becomes straightforward. This approach can be applied to larger applications where robust data handling is essential, making Kotlin and Jetpack Compose a powerful combination 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


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


@Composable
fun MainContent() {
    val format = Json{
        isLenient = true
        // handle null values
        coerceInputValues = true
    }

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

    val userString2 = "{ firstName: Sofia," +
            "lastName: Khatun, age: 27 }"
    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(
    val firstName: String,
    val lastName: String = "Default last name",
    val age: Int
)
More android jetpack compose tutorials