skip to Main Content

I’m trying to learn Android Development by making a bus arrival timing application that makes API calls to a local API that has the arrival timings for the next 3 buses. I am using Kotlins and Jetpack Compose to help. This is a sample of the JSON response I get:

{
    "odata.metadata": "http://datamall2.mytransport.sg/ltaodataservice/$metadata#BusArrivalv2/@Element",
    "BusStopCode": "65199",
    "Services": [
        {
            "ServiceNo": "89",
            "Operator": "SBST",
            "NextBus": {
                "OriginCode": "64009",
                "DestinationCode": "64009",
                "EstimatedArrival": "2022-12-22T22:15:06+08:00",
                "Latitude": "1.3947326666666666",
                "Longitude": "103.89898083333334",
                "VisitNumber": "1",
                "Load": "SEA",
                "Feature": "WAB",
                "Type": "DD"
            },
            "NextBus2": {
                "OriginCode": "64009",
                "DestinationCode": "64009",
                "EstimatedArrival": "2022-12-22T22:31:36+08:00",
                "Latitude": "0",
                "Longitude": "0",
                "VisitNumber": "1",
                "Load": "SEA",
                "Feature": "WAB",
                "Type": "DD"
            },
            "NextBus3": {
                "OriginCode": "64009",
                "DestinationCode": "64009",
                "EstimatedArrival": "2022-12-22T22:47:51+08:00",
                "Latitude": "0",
                "Longitude": "0",
                "VisitNumber": "1",
                "Load": "SEA",
                "Feature": "WAB",
                "Type": "DD"
            }
        }
    ]
}

I have been following the code labs on Android Documentation and tried to store the results into a Data Class in Android as shown below. I don’t think I am doing it right for the JSON objects NextBus, NextBus2 and NextBus3. Thank you

SingaporeBus.kt

package com.example.busexpress.network

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class SingaporeBus(
    @SerialName(value = "ServiceNo")
    val busServiceNumber: String,

    // Bus Arrival Timings
    @SerialName(value = "NextBus")
    val nextBus1: Json,

    @SerialName(value = "NextBus2")
    val nextBus2: Json,

    @SerialName(value = "NextBus3")
    val nextBus3: Json
)

@Serializable
data class NextBusTiming(
    // Date-Time expressed in the UTC standard, GMT+8 for Singapore Standard Time (SST)
    @SerialName(value = "EstimatedArrival")
    val estimatedArrival: String,

    @SerialName(value = "OriginCode")
    val startingBusStop: String,

    @SerialName(value = "DestinationCode")
    val endingBusStop: String,

    // Current Bus Occupancy Levels
    @SerialName(value = "Load")
    val busOccupancyLevels: String,

    // Wheelchair Support
    @SerialName(value = "Feature")
    val wheelchairAccessible: String,

    // Bus Type
    @SerialName(value = "Type")
    val vehicleType: String,

    // Bus Approximate Location
    @SerialName(value = "Latitude")
    val latitude: String,

    @SerialName(value = "Longitude")
    val longitude: String
)

2

Answers


  1. Each object stored in Json should have its own representation in data class.

    kotlinx.serialization automatically handles nested objects.

    So your SingaporeBus model should look like this:

    @Serializable
    data class SingaporeBus(
        @SerialName(value = "ServiceNo")
        val busServiceNumber: String,
    
        // Bus Arrival Timings
        @SerialName(value = "NextBus")
        val nextBus1: NextBusTiming,
    
        @SerialName(value = "NextBus2")
        val nextBus2: NextBusTiming,
    
        @SerialName(value = "NextBus3")
        val nextBus3: NextBusTiming
    )
    
    Login or Signup to reply.
  2. I hope this answer helps you
    The code you’ve written should be like this

    @Serializable
    data class SingaporeBus(
        @SerialName(value = "ServiceNo")
        val busServiceNumber: String,
    
        // Bus Arrival Timings
        @SerialName(value = "NextBus")
        val nextBus1: NextBusTiming,
    
        @SerialName(value = "NextBus2")
        val nextBus2: NextBusTiming,
    
        @SerialName(value = "NextBus3")
        val nextBus3: NextBusTiming
    )
    @Serializable
    data class NextBusTiming(
        // Date-Time expressed in the UTC standard, GMT+8 for Singapore Standard Time (SST)
        @SerialName(value = "EstimatedArrival")
        val estimatedArrival: String,
    
        @SerialName(value = "OriginCode")
        val startingBusStop: String,
    
        @SerialName(value = "DestinationCode")
        val endingBusStop: String,
    
        // Current Bus Occupancy Levels
        @SerialName(value = "Load")
        val busOccupancyLevels: String,
    
        // Wheelchair Support
        @SerialName(value = "Feature")
        val wheelchairAccessible: String,
    
        // Bus Type
        @SerialName(value = "Type")
        val vehicleType: String,
    
        // Bus Approximate Location
        @SerialName(value = "Latitude")
        val latitude: String,
    
        @SerialName(value = "Longitude")
        val longitude: String
    )
    

    Although The code can hove some improvements :

    1.If you used Retrofit you would be able to use @SerializedName instead of @SerialName and in that situation your class should use

    @Parcelize and the data class implemented Parcelable interface

    this is retrofit sample for your api call :

    @Parcelize
    data class SingaporeBus(
        @SerializedName(value = "ServiceNo")
        val busServiceNumber: String,
    
        // Bus Arrival Timings
        @SerializedName(value = "NextBus")
        val nextBus1: NextBusTiming,
    
        @SerializedName(value = "NextBus2")
        val nextBus2: NextBusTiming,
    
        @SerializedName(value = "NextBus3")
        val nextBus3: NextBusTiming
    ) : Parcelable
    
    @Parcelize
    data class NextBusTiming(
        @SerializedName(value = "EstimatedArrival")
        val estimatedArrival: String,
    
        @SerializedName(value = "OriginCode")
        val startingBusStop: String,
    
        @SerializedName(value = "DestinationCode")
        val endingBusStop: String,
    
        @SerializedName(value = "Load")
        val busOccupancyLevels: String,
    
        @SerializedName(value = "Feature")
        val wheelchairAccessible: String,
    
        @SerializedName(value = "Type")
        val vehicleType: String,
    
        @SerializedName(value = "Latitude")
        val latitude: String,
    
        @SerializedName(value = "Longitude")
        val longitude: String
    ) : Parcelable
    

    2.It’s better to not use value declaration in for example in
    @SerialName(value = "ServiceNo")

    3.If you access the server json fields shouldn’t start by Caps for example "ServicesNo" and they could be camelCase

    4.According to Uncle Bob naming of fields should be clear and doesn’t have comments like this :

        @Serializable
    data class NextBusTiming(
        @SerialName(value = "EstimatedArrival")
        val estimatedArrival: String,
    
        @SerialName(value = "OriginCode")
        val startingBusStop: String,
    
        @SerialName(value = "DestinationCode")
        val endingBusStop: String,
        
        @SerialName(value = "Load")
        val busOccupancyLevels: String,
        
        @SerialName(value = "Feature")
        val wheelchairAccessible: String,
    
        @SerialName(value = "Type")
        val vehicleType: String,
    
        @SerialName(value = "Latitude")
        val latitude: String,
    
        @SerialName(value = "Longitude")
        val longitude: String
    )
    

    5.Finally you can have different classes for use them easier at other Classes for example NextBusTiming may have other usage in future and in this you can find it better than being in same class or Kotlin files actually.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search