Parsing XML data via Retrofit 2.x using Kotlin
For most part of fetching data through network calls, we are used to getting data in JSON. However, there can be instances when we need to or have to get data in XML format, for example when you need to parse an RSS feed. Through this tutorial, I’ll show you how that can be achieved via Retrofit using Kotlin.
Enter SimpleXmlConverterFactory
, a data converter provided by Retrofit only. Though this converter factory is deprecated, but that doesn’t mean it’s non-functional. For most things, it gets tasks done simply. However, you can also check out Tickaroo library for XML parsing. It pretty much works similarly to SimpleXmlConverterFactory
.
I’ll be taking example of Google Trends API which returns the data in format something like this -
<rss version=”2.0">
<channel>
<title>Daily Search Trends</title>
<description>Recent searches</description>
<link>
https://trends.google.com/trends/trendingsearches/daily?geo=US
</link>
<atom:link href=”https://trends.google.com/trends/trendingsearches/daily/rss?geo=US" rel=”self” type=”application/rss+xml”/>
<item>
<title>Pride month</title>
<ht:approx_traffic>50,000+</ht:approx_traffic>
<description/>
<link>
https://trends.google.com/trends/trendingsearches/daily?geo=US#Pride%20month
</link>
<pubDate>Mon, 01 Jun 2020 01:00:00 -0700</pubDate>
<ht:picture>
https://t1.gstatic.com/images?q=tbn:ANd9GcRl-PZIiwOHNaIdSSUTUjHTp32TLNzz4nv2xouis0RlugNrD-PFoFl1Tl0me2sgUZNSFy6TgpA_
</ht:picture>
</item>
</channel>
</rss>
I’ve deliberately skipped some of the fields to keep it simple. You can check out the complete response here. Alright, let’s get on with the implementation.
STEP 1:
Add the following dependencies in gradle script:
api ‘com.squareup.retrofit:retrofit:2.4.0’
api ‘com.squareup.retrofit:converter-simplexml:2.4.0’
STEP 2:
Time for model classes -
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
@Root(name = "rss", strict = false)
class TrendingSearchResponseWrapper @JvmOverloads constructor(
@field: Element(name = "channel")
var channel: TrendingSearchResponse? = null
)
@Root(name = "channel", strict = false)
class TrendingSearchResponse @JvmOverloads constructor(
@field: ElementList(inline = true)
var itemList: List<TrendingSearchItem>? = null
)
@Root(name = "item", strict = false)
class TrendingSearchItem @JvmOverloads constructor(
@field: Element(name = "title")
var title: String = "",
@field: Element(name = "description", required = false)
var description: String = "",
@field: Element(name = "link")
var link: String = ""
)
This was the tricky section for me since XmlConverter expects model class to satisfy some rules which in some cases are not easily caught. There are some important things to note here -
1. Notice how we used Root
, Element
and ElementList
annotations as we go about parsing the nested XML.
2. Setting strict=false
only works when you are using XmlConverter factory in non-strict mode.
3. Use required=false
for fields that might not be present in some the cases. Otherwise, the converter throws an error.
4. Very importantly, use JvmOverload
annotation and make sure you have given a default value to every field. XmlConverterFactory requires that you provide every possible constructor for your model class. JvmOverload
annotation takes care of that for us.
STEP 3:
Next up, create service interface that will be used by Retrofit to execute our request.
import io.reactivex.Single
import retrofit2.http.GET
import retrofit2.http.Query
interface TrendingSearchesApi {
@GET("rss")
fun getTrendingSearches(@Query("geo") countryCode: String): Single<TrendingSearchResponseWrapper>
}
STEP 4:
Completing the connection by adding XmlConverterFactory while creating Retrofit builder —
val retrofit : Retrofit = Retrofit.Builder()
.baseUrl("https://trends.google.com/trends/trendingsearches /daily/")
.client(OkHttpClient())
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();val trendingApi = retrofit.create(TrendingSearchesApi.class);
In case you add multiple converters for Retrofit Builder, it will try to parse the response with first added converter. If that fails, then next and so on…
And that’s it, simple and sweet. Comment or reach out in case you have any queries.
If you found this informative, hit the CLAP
button. Thanks!