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
)
CodePudding user response:
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
)
CodePudding user response:
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.