fbpx
How to parse JSON with Retrofit Converters using Kotlin

How to parse JSON with Retrofit Converters using Kotlin

In this tutorial, I will show you how to parse JSON using the following converters with Retrofit:

  • Gson converter
  • Moshi converter
  • Kotlinx Serialization converter
  • Jackson converter

I’m not going to explain how to set up Retrofit and make HTTP requests because I already did it in this tutorial, but only how to parse JSON with these converters.

I’m also going to cover 3 cases may you face when you’re trying to parse JSON:

  • Simple JSON: Just a simple JSON without any complex structure
  • Array JSON: The JSON structure begins with an array and without a key
  • Nested JSON: Includes nested objects

Adding the library

Go to your app-level build.gradle file, and add the converter

Gson Converter

dependencies {
    // (Coroutines, Retrofit)...

    // Gson Converter
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

    // ...
}

Moshi Converter

dependencies {
    // (Coroutines, Retrofit)...

    // Moshi Converter
    implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'

    // ...
}

Kotlinx Serialization Converter

apply plugin: 'kotlinx-serialization'

// ...

dependencies {
    // (Coroutines, Retrofit)...

    // Kotlinx Serialization Converter
    implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.7.0'

    // ...
}

And in the top-level build.gradle file add the classpath:

buildscript {
    // ...
    dependencies {
        // ...
        classpath "org.jetbrains.kotlin:kotlin-serialization:1.4.10"
    }
}

Jackson Converter

dependencies {
    // (Coroutines, Retrofit)...

    // Jackson Converter
    implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'

    // ...
}

Adding the Converter in Retrofit

In your Retrofit instance, you can add the converter by adding the line:
.addConverterFactory(THE_CONVERTER_YOU_PREFER)

val retrofit = Retrofit.Builder()
    .baseUrl("http://api.example.com/")
    .addConverterFactory(THE_CONVERTER_YOU_PREFER)
    .build()

Gson Converter

.addConverterFactory(GsonConverterFactory.create())

Moshi Converter

.addConverterFactory(MoshiConverterFactory.create())

Kotlinx Serialization Converter:

.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))

Jackson Converter

.addConverterFactory(JacksonConverterFactory.create())

Parsing simple JSON

A simple JSON doesn’t have any complex structure, like arrays, nested objects, e.t.c, and it looks like that:

{
    "id": "1",
    "employee_name": "Jack Full",
    "employee_salary": "300800",
    "employee_age": "61"
}

Create a new data class file with the following fields:

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class SimpleJSONModel(

    // Use @SerializedName(" ") for the Gson converter
    // @field:Json(name = " ") for the Moshi converter
    // @SerialName(" ") for the Kotlinx Serialization converter
    // @JsonProperty(" ") for the Jackson converter

    @SerializedName("id")
    var employeeId: String?,

    @SerializedName("employee_name")
    var employeeName: String?,

    @SerializedName("employee_salary")
    var employeeSalary: String?,

    @SerializedName("employee_age")
    var employeeAge: String?

)

If the model’s variable names are different from the JSON field names (In this example, employeeId is different from the “id”), then use the converter’s annotation and indicate the field’s name in JSON.

In this example, we’re using the Gson converter, so the annotation for this is @SerializedName).

Next, in the APIService.kt file, add your model:

interface APIService {
    // ...

    @GET("/johncodeos-blog/ParseJSONRetrofitConvertersExample/main/simple.json")
    suspend fun getEmployee(): Response<SimpleJSONModel>

    // ...
}

And lastly, your GET request:

fun parseJSON() {

    // Create Retrofit
    val retrofit = Retrofit.Builder()
        .baseUrl("https://raw.githubusercontent.com")
        .addConverterFactory(THE_CONVERTER_YOU_PREFER)
        .build()

    // Create Service
    val service = retrofit.create(APIService::class.java)
    CoroutineScope(Dispatchers.IO).launch {

        // Do the GET request and get response
        val response = service.getEmployees()

        withContext(Dispatchers.Main) {
            if (response.isSuccessful) {

                val items = response.body()
                if (items != null) {
                    for (i in 0 until items.count()) {
                        // ID
                        val id = items[i].employeeId ?: "N/A"
                        Log.d("ID: ", id)

                        // Employee Name
                        val employeeName = items[i].employeeName ?: "N/A"
                        Log.d("Employee Name: ", employeeName)

                        // Employee Salary
                        val employeeSalary = items[i].employeeSalary ?: "N/A"
                        Log.d("Employee Salary: ", employeeSalary)

                        // Employee Age
                        val employeeAge = items[i].employeeAge ?: "N/A"
                        Log.d("Employee Age: ", employeeAge)
                        
                    }
                }

            } else {

                Log.e("RETROFIT_ERROR", response.code().toString())

            }
        }
    }
}

Parsing array JSON

JSON uses arrays to create a list of data. Some of them start with an array but without a key:

[
    {
        "id": "1",
        "employee_name": "Tiger Nixon",
        "employee_salary": "320800",
        "employee_age": "61"
    },
    {
        "id": "2",
        "employee_name": "Garrett Winters",
        "employee_salary": "170750",
        "employee_age": "63"
    },
    // ...
]

Create a new data class file with the following fields (It’s the same with the simple JSON model):

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class ArrayJSONModel(

    // Use @SerializedName(" ") for the Gson converter
    // @field:Json(name = " ") for the Moshi converter
    // @SerialName(" ") for the Kotlinx Serialization converter
    // @JsonProperty(" ") for the Jackson converter

    @SerializedName("id")
    var employeeId: String?,

    @SerializedName("employee_name")
    var employeeName: String?,

    @SerializedName("employee_salary")
    var employeeSalary: String?,

    @SerializedName("employee_age")
    var employeeAge: String?

)

Next, in the APIService.kt file, add a List with the model:

interface APIService {
    // ...

    @GET("/johncodeos-blog/ParseJSONRetrofitConvertersExample/main/array.json")
    suspend fun getEmployees(): Response<List<ArrayJSONModel>>

    // ...
}

Then, your GET request:

fun parseJSON() {

    // Create Retrofit
    val retrofit = Retrofit.Builder()
        .baseUrl("https://raw.githubusercontent.com")
        .addConverterFactory(THE_CONVERTER_YOU_PREFER)
        .build()

    // Create Service
    val service = retrofit.create(APIService::class.java)
    CoroutineScope(Dispatchers.IO).launch {

        // Do the GET request and get response
        val response = service.getEmployees()

        withContext(Dispatchers.Main) {
            if (response.isSuccessful) {

                val items = response.body()
                if (items != null) {
                    for (i in 0 until items.count()) {
                        // ID
                        val id = items[i].employeeId ?: "N/A"
                        Log.d("ID: ", id)

                        // Employee Name
                        val employeeName = items[i].employeeName ?: "N/A"
                        Log.d("Employee Name: ", employeeName)

                        // Employee Salary
                        val employeeSalary = items[i].employeeSalary ?: "N/A"
                        Log.d("Employee Salary: ", employeeSalary)

                        // Employee Age
                        val employeeAge = items[i].employeeAge ?: "N/A"
                        Log.d("Employee Age: ", employeeAge)
                        
                    }
                }

            } else {

                Log.e("RETROFIT_ERROR", response.code().toString())

            }
        }
    }
}

Parsing nested JSON

When a JSON object is inside another JSON object, it’s called ‘nested’ and will look like the following JSON structure:

{
    "data": [
        {
            "id": "1",
            "employee": {
                "name": "Tiger Nixon",
                "salary": {
                    "usd": 320800,
                    "eur": 273545
                },
                "age": "61"
            }
        },
        {
            "id": "2",
            "employee": {
                "name": "Garrett Winters",
                "salary": {
                    "usd": 170750,
                    "eur": 145598
                },
                "age": "63"
            }
        },
        // ...
    ]
}

First, create a new data class file, and make a new data class for each nested object:

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class NestedJSONModel(

    // Use @SerializedName(" ") for the Gson converter
    // @field:Json(name = " ") for the Moshi converter
    // @SerialName(" ") for the Kotlinx Serialization converter
    // @JsonProperty(" ") for the Jackson converter

    var data: List<Data>?

)

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class Data(

    @SerializedName("id")
    val employeeId: String?,

    val employee: Employee?

)

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class Employee(

    val name: String?,

    val salary: Salary?,

    val age: String?

)

// Uncomment the follow line if you're using the Kotlinx Serialization converter
// @Serializable
data class Salary(

    val eur: Int?,

    val usd: Int?

)

Next, in the APIService.kt file, add the model as below:

interface APIService {
    // ...

    @GET("/johncodeos-blog/ParseJSONRetrofitConvertersExample/main/nested.json")
    suspend fun getEmployeesNested(): Response<NestedJSONModel>

    // ...
}

And lastly, your GET request:

@SuppressLint("LongLogTag")
fun parseJSON() {
    // Create Retrofit
    val retrofit = Retrofit.Builder()
        .baseUrl("https://raw.githubusercontent.com")
        .addConverterFactory(THE_CONVERTER_YOU_PREFER)
        .build()

    // Create Service
    val service = retrofit.create(APIService::class.java)

    CoroutineScope(Dispatchers.IO).launch {

        // Do the GET request and get response
        val response = service.getEmployeesNested()

        withContext(Dispatchers.Main) {
            if (response.isSuccessful) {

                val items = response.body()?.data
                if (items != null) {
                    for (i in 0 until items.count()) {
                        // ID
                        val id = items[i].employeeId ?: "N/A"
                        Log.d("ID: ", id)

                        // Employee Name
                        val employeeName = items[i].employee?.name ?: "N/A"
                        Log.d("Employee Name: ", employeeName)

                        // Employee Salary in USD
                        val employeeSalaryUSD = items[i].employee?.salary?.usd ?: 0
                        Log.d("Employee Salary in USD: ", employeeSalaryUSD.toString())

                        // Employee Salary in EUR
                        val employeeSalaryEUR = items[i].employee?.salary?.eur ?: 0
                        Log.d("Employee Salary in EUR: ", employeeSalaryEUR.toString())

                        // Employee Age
                        val employeeAge = items[i].employee?.age ?: "N/A"
                        Log.d("Employee Age: ", employeeAge)
                        
                    }
                }

            } else {

                Log.e("RETROFIT_ERROR", response.code().toString())

            }
        }
    }
}
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments