Simplifying API consumption to Android Apps from Basic to Advanced — part2: JSON and XML

André Figas
3 min readNov 3, 2022

--

Now we have ways to send information from client to server, I wouldn’t expect receiving a simple “hello world” would satisfy the necessities of your project.

JSON

If you are facing something like

{
“latitude”: 53.5,
“longitude”: 14.219999,
“generationtime_ms”: 1.5190839767456055,
“utc_offset_seconds”: 0,
“timezone”: “GMT”,
“timezone_abbreviation”: “GMT”,
“elevation”: 35
}

You have to parse from .json to your model class. First of all, do not write your model manually. You can find on the internet many json2<language>. So enjoy it.
For this sample, I used json2kt.com to generate JSON files. But there are many options to do the same.

Model Class

data class Location(    @SerializedName("latitude")
var latitude: Double? = null,
@SerializedName("longitude")
var longitude: Double? = null,
@SerializedName("generationtime_ms")
var generationtimeMs: Double? = null,
@SerializedName("utc_offset_seconds")
var utcOffsetSeconds: Int? = null,
@SerializedName("timezone")
var timezone: String? = null,
@SerializedName("timezone_abbreviation")
var timezoneAbbreviation: String? = null,
@SerializedName("elevation")
var elevation: Int? = null
)

Adding library

implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

API

Our API Interface returns our class instead of a ResponseBody

interface Api {
@GET("/v1/forecast")
fun getLocation(@Query("latitude") latitude : Double,
@Query("longitude") longitude : Double)
: Call<Location>
}

Adding converter

Retrofit.Builder()
.baseUrl("https://api.open-meteo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(Api::class.java)
.getLocation(53.0, 14.0)
.enqueue(object : Callback<Location> {
override fun onResponse(call: Call<Location>,
response: Response<Location>) {
val location : Location? = response.body()
println(location)
}
override fun onFailure(call: Call<Location>, t: Throwable) {
t.printStackTrace()
}
})

This will be enough to run your project in debug mode, but some classes stop working after being encrypted. If not be clear to you why we are talking about encryption, basically you won’t wish to expose your code when you send your code to GooglePlay. Ok, you send them only a compiled project, but there are some reverse engineering tools.

android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-rules.pro'),
'proguard-rules.pro'
}
}
}

When you enable the minify flag for a specific build type, that will produce an encrypted code, but sometimes it breaks our project. So, we can skip some stuff from our encryption based on our proguard-rules.pro. Some projects required some entries in that file.

Gson Proguard

XML

If you are facing something like

<channel id="1">
<title>W3Schools Home Page</title>
<link>https://www.w3schools.com</link>
<description>Free web building tutorials</description>
<item>
<title>RSS Tutorial</title>
<link>https://www.w3schools.com/xml/xml_rss.asp</link>
<description>New RSS tutorial on W3Schools</description>
</item>
<item>
<title>XML Tutorial</title>
<link>https://www.w3schools.com/xml</link>
<description>New XML tutorial on W3Schools</description>
</item>
</channel>

You need an XML Parser. To parse a JSON file, there is one main library (Gson), as you saw that json2Kotlin brought a SerializedName annotation, and that belongs to the Gson library (alternatively, I’d recommend you look for Moshi), but here, when we handle an XML, unfortunately, there is not one main library, so I will use here the Tickaroo library but fill free to look another one. I just explained why maybe you will find some difficult to find a good “xml2Kt” online converter, then I recommend you write each line of your model class.

Adding kapt support

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}

Adding libraries

implementation 'com.tickaroo.tikxml:retrofit-converter:0.8.13'
implementation 'com.tickaroo.tikxml:annotation:0.8.13'
kapt 'com.tickaroo.tikxml:processor:0.8.13'

Model Class

@Xml(name = "channel")
class Channel(
@Attribute(name = "id")
val id : String? = null,
@PropertyElement(name = "title")
val title: String? = null,
@PropertyElement(name = "link")
val link: String? = null,
@PropertyElement(name = "description")
val description: String? = null,
@Element
val items : List<Item> = emptyList()
)
@Xml(name = "item")
data class Item(
@PropertyElement(name = "title")
val title: String? = null,
@PropertyElement(name = "link")
val link: String? = null,
@PropertyElement(name = "description")
val description: String? = null
)

API

interface Api {    @GET("v3/5df31685-22de-4f94-9cb2-58b36a8375e9")
fun getChannel()
: Call<Channel>
}

Adding Converter

Retrofit.Builder()
.baseUrl("https://run.mocky.io/")
.addConverterFactory(TikXmlConverterFactory.create())
.build().create(Api::class.java)
.getChannel()
.enqueue(object : Callback<Channel> {
override fun onResponse(call: Call<Channel>,
response: Response<Channel>) {
val channel : Channel? = response.body()
println(channel)
}
override fun onFailure(call: Call<Channel>, t: Throwable) {
t.printStackTrace()
}
})

Tickaroo Proguard

--

--