------------------------- | User List | ------------------------- | • John Doe | | john@example.com | | • Jane Smith | | jane@example.com | -------------------------
JSON parsing with Gson/Moshi in Android Kotlin - Mini App: Build & Ship
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp class UserListActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { UserListScreen() } } } @Composable fun UserListScreen() { // TODO: Add state for loading, error, and user list // TODO: Parse JSON string using Gson or Moshi // TODO: Show loading indicator while parsing // TODO: Show error message if parsing fails // TODO: Show list of users when parsing succeeds }
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.google.gson.Gson import com.google.gson.reflect.TypeToken class UserListActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { UserListScreen() } } } data class User(val name: String, val email: String) @Composable fun UserListScreen() { val jsonString = """[ {\"name\": \"John Doe\", \"email\": \"john@example.com\"}, {\"name\": \"Jane Smith\", \"email\": \"jane@example.com\"} ]""" var users by remember { mutableStateOf<List<User>?>(null) } var isLoading by remember { mutableStateOf(true) } var errorMessage by remember { mutableStateOf<String?>(null) } LaunchedEffect(Unit) { try { val listType = object : TypeToken<List<User>>() {}.type users = Gson().fromJson(jsonString, listType) } catch (e: Exception) { errorMessage = "Failed to parse JSON" } finally { isLoading = false } } Scaffold( topBar = { TopAppBar(title = { Text("User List") }) } ) { paddingValues -> Box(modifier = Modifier .fillMaxSize() .padding(paddingValues)) { when { isLoading -> { CircularProgressIndicator(modifier = Modifier.align(androidx.compose.ui.Alignment.Center)) } errorMessage != null -> { Text( text = errorMessage ?: "", color = MaterialTheme.colorScheme.error, modifier = Modifier.align(androidx.compose.ui.Alignment.Center) ) } users != null -> { LazyColumn(modifier = Modifier.fillMaxSize()) { items(users!!) { user -> Column(modifier = Modifier .fillMaxWidth() .padding(16.dp)) { Text(text = user.name, fontWeight = FontWeight.Bold) Text(text = user.email) } Divider() } } } } } } }
This solution uses the Gson library to parse a JSON string into a list of User data objects. We define a User data class with name and email fields matching the JSON keys.
In the UserListScreen composable, we keep track of three states: isLoading to show a loading spinner, errorMessage to show an error if parsing fails, and users to hold the parsed list.
We use LaunchedEffect to parse the JSON once when the screen loads. If parsing succeeds, we update users. If it fails, we set an error message.
The UI shows a top app bar with the title. Then it shows a loading spinner while parsing, an error message if parsing fails, or a scrollable list of users if parsing succeeds. Each user item shows the name in bold and the email below.
This approach cleanly separates loading, error, and success states and uses Compose best practices for state and UI updates.
------------------------- | User List | ------------------------- | • John Doe | | john@example.com | |----------------------- | • Jane Smith | | jane@example.com | -------------------------
Practice
Solution
Step 1: Understand JSON parsing role
Gson and Moshi are libraries that help convert JSON strings into Kotlin objects for easier data handling.Step 2: Identify the correct purpose
They do not create UI, manage navigation, or handle databases directly.Final Answer:
To convert JSON text into Kotlin objects -> Option AQuick Check:
JSON parsing = Convert JSON to objects [OK]
- Thinking Gson/Moshi create UI components
- Confusing JSON parsing with database handling
- Assuming they manage app navigation
{"name":"Alice","age":30}Solution
Step 1: Match JSON keys and types
The JSON has keys "name" (String) and "age" (Int).Step 2: Check Kotlin data class syntax
Correct syntax uses 'data class' with val properties and types matching JSON.Final Answer:
data class Person(val name: String, val age: Int) -> Option BQuick Check:
Data class with val and correct types = data class Person(val name: String, val age: Int) [OK]
- Using var instead of val unnecessarily
- Wrong property types (e.g., age as String)
- Missing 'val' keyword in data class
val json = "{\"name\":\"Bob\", \"age\":25}"
val person = Gson().fromJson(json, Person::class.java)
println(person.name)What will be printed?
Solution
Step 1: Parse JSON to Person object
Gson converts the JSON string into a Person object with name="Bob" and age=25.Step 2: Print the name property
println(person.name) outputs the name value, which is "Bob".Final Answer:
Bob -> Option DQuick Check:
person.name = "Bob" [OK]
- Printing age instead of name
- Expecting class name instead of property value
- Assuming null if parsing fails
val json = "{\"name\":\"Eve\", \"age\":28}"
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(Person::class.java)
val person = adapter.fromJson(json)
println(person?.name)Assuming Person is a correct data class, why might this code fail at runtime?
Solution
Step 1: Check Moshi setup for Kotlin data classes
Moshi requires adding the KotlinJsonAdapterFactory to support Kotlin features like default values and nullability.Step 2: Identify missing adapter
The code builds Moshi without .add(KotlinJsonAdapterFactory()), so parsing may fail or produce null.Final Answer:
Missing Moshi Kotlin adapter for data class support -> Option CQuick Check:
Moshi needs KotlinJsonAdapterFactory for data classes [OK]
- Not adding KotlinJsonAdapterFactory
- Assuming JSON string is invalid
- Confusing println with logging
[{"id":1,"title":"Task 1"},{"id":2,"title":"Task 2"}]How do you parse it into a Kotlin List<Task> using Gson, assuming
data class Task(val id: Int, val title: String)?Solution
Step 1: Understand parsing JSON arrays
Gson needs a Type object to parse generic types like List<Task>.Step 2: Use TypeToken to get List<Task> type
TypeToken() {}.type provides the correct type for Gson.fromJson to parse the array.Final Answer:
Use TypeToken() {}.type with Gson.fromJson -> Option AQuick Check:
Use TypeToken for generic list parsing [OK]
- Parsing array as single object
- Manually parsing instead of using Gson
- Using toJson instead of fromJson
Ready to prove it?
Pass the test and this pattern moves unknown
