Day94 of #100DaysOfCode
Hii folks 🙌
Today I will be continuing the same pathway in which we will learn to parse the JSON response with Moshi
Unit 4: Internet
Pathway 2: Get and Display Data
https://developer.android.com/courses/android-basics-kotlin/course
Parse the JSON response with Moshi
Add Moshi library dependencies
- We will open
build.gradle
(Module: app). - In the dependencies section, we will add the code shown below to include the Moshi dependency. This dependency adds support for the Moshi JSON library with Kotlin support.
// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
- Locate the lines for the Retrofit scalar converter in the
dependencies
block and change these dependencies to useconverter-moshi
:
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
with this
// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
- Now, Click Sync Now to rebuild the project with the new dependencies.
Implement the Mars Photos data class
- A sample entry of the JSON response we get from the web service looks something like this:
[{
"id":"424906",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"
},
...]
In the example above, we notice that each Mars photo entry has these JSON key and value pairs:
id
: the ID of the property, as a string. Since it is wrapped in" "
it is of the typeString
notInteger
.img_src
: The image's URL as a string.
Moshi parses this JSON data and converts it into Kotlin objects. To do this, Moshi needs to have a Kotlin data class to store the parsed results, so in this step we will create the data class,
MarsPhoto
.
- Then we do Right-click on the network package and select
New > Kotlin File/Class.
- In the popup, select Class and enter
MarsPhoto
as the name of the class. This creates a new file calledMarsPhoto.kt
in thenetwork
package. - Make
MarsPhoto
a data class by adding thedata
keyword before the class definition. Change the {} braces to () parentheses. This leaves us with an error, because data classes must have at least one property defined.
data class MarsPhoto(
)
* We will add the following properties to the MarsPhoto
class definition.
data class MarsPhoto(
val id: String, val img_src: String
)
When Moshi parses the JSON, it matches the keys by name and fills the data objects with appropriate values.
@Json Annotation
To use variable names in the data class that differ from the key names in the JSON response, use the @Json
annotation. In this example, the name of the variable in the data class is imgSrcUrl
. The variable can be mapped to the JSON attribute img_src
using @Json(name = "img_src")
.
- Replace the line for the
img_src
key with the line shown below. Importcom.squareup.moshi.Json
when requested.
@Json(name = "img_src") val imgSrcUrl: String
Update MarsApiService and OverviewViewModel
In this task, we will create a Moshi object using the Moshi Builder, similar to the Retrofit builder.
We will replace ScalarsConverterFactory
with the KotlinJsonAdapterFactory
to let Retrofit know it can use Moshi to convert the JSON response into Kotlin objects. We will then update the network API and ViewModel
to use the Moshi object.
- Open
network/MarsApiService.kt
. Notice the unresolved reference errors forScalarsConverterFactory
. This is because of the Retrofit dependency change we made in a previous step. Delete the import forScalarConverterFactory
.
Remove:
import retrofit2.converter.scalars.ScalarsConverterFactory
- At the top of the file, just before the Retrofit builder, add the following code to create the Moshi object, similar to the Retrofit object.
private val moshi = Moshi.Builder()
Import com.squareup.moshi.Moshi
and com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
when requested.
- For Moshi’s annotations to work properly with Kotlin, in the Moshi builder, add the
KotlinJsonAdapterFactory
, and then callbuild()
.
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
- In the
retrofit
object declaration change the Retrofit builder to use theMoshiConverterFactory
instead of theScalarConverterFactory
, and pass in themoshi
instance we just created.
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
Import retrofit2.converter.moshi.MoshiConverterFactory
when requested.
- Now that we have the
MoshiConverterFactory
in place, we can ask Retrofit to return a list ofMarsPhoto
objects from the JSON array instead of returning a JSON string. Update theMarsApiService
interface to have Retrofit return a list ofMarsPhoto
objects, instead of returningString
.
interface MarsApiService {
@GET("photos")
suspend fun getPhotos(): List<MarsPhoto>
}
- Do similar changes to the
viewModel
, openOverviewViewModel.kt
. Scroll down togetMarsPhotos()
method. - In the method
getMarsPhotos()
,listResult
is aList<MarsPhoto>
not aString
anymore. The size of that list is the number of photos that were received and parsed. To print the number of photos retrieved update_status.value
as follows.
_status.value = "Success: ${listResult.size} Mars photos retrieved"
Import com.example.android.marsphotos.network.MarsPhoto
when prompted.
- Make sure airplane mode is turned off in our device or emulator. Compile and run the app. This time the message should show the number of properties returned from the web service, not a big JSON string
That is all for Day94 ✅
Thanks for reading, See you tomorrow!