Sometimes (very rarely, but still) you have to work with a service that sends data to both Json and XML. Here’s how to do it
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
implementation 'com.squareup.retrofit2:converter-jaxb:2.4.0'
There are coroutines (why not?), Retrofit and two converters: Moshi for Json and Jaxb for XML
Now let’s create two annotations:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention()
internal annotation class Json
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention()
internal annotation class Xml
Main magic – coverters factory:
class XmlOrJsonConverterFactory : Converter.Factory() {
override fun responseBodyConverter(type: Type?, annotations: Array<Annotation>?, retrofit: Retrofit?): Converter<ResponseBody, *>? {
annotations?.forEach { annotation ->
when (annotation.annotationClass) {
Xml::class.java -> return JaxbConverterFactory.create().responseBodyConverter(type, annotations, retrofit)
Json::class.java -> return MoshiConverterFactory.create().responseBodyConverter(type, annotations, retrofit)
}
}
return MoshiConverterFactory.create().responseBodyConverter(type, annotations, retrofit)
}
companion object {
fun create() = XmlOrJsonConverterFactory()
}
}
Retrofit API:
interface JsonPlaceholderApi {
@Json
@GET("/posts")
fun getPosts(): Deferred<Response<List<Post>>>
companion object {
private const val BASE_URL = "https://jsonplaceholder.typicode.com"
fun getApi(): JsonPlaceholderApi = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(XmlOrJsonConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
.create(JsonPlaceholderApi::class.java)
}
}
And also one small data-class:
data class Post(
val userId: Long,
val id: Long,
val title: String,
val body: String
): Serializable
And this is all. Activity, launch, log results, applause:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch(Dispatchers.Main) {
val postsRequest = JsonPlaceholderApi.getApi().getPosts()
val postsResponse = postsRequest.await()
postsResponse.body()?.forEach { post -> Log.d("POSTS", post.title) }
}
}
}
And one more thing. Do not forget the premission for network access in the manifest:
<uses-permission android:name="android.permission.INTERNET" />
Code on my GitLab.