Reputation: 3215
I am building an app need to pull some data from 2 endpoints: /api/products
and /api/type
The first endpoint return a JSON and this is working fine. However, the second endpoint does not return a json but an Array of object.
The response sent back by api/type
looks like this :
[["Model",123],["ModelB",456],["ModelC",789]]
when the other endpoints return the "usual" json.
I am using retrofit, okhttp and dagger-hilt.
The retrofit module is setup as below:
@InstallIn(SingletonComponent::class)
@Module
class APIModule {
@Singleton
@Provides
@Named("default")
fun provideDefaultOkHttpClient(): OkHttpClient =
OkHttpClient
.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build()
@Singleton
@Provides
fun provideRetrofit(
@Named("default") okHttpClient: OkHttpClient
): Retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://test.com")
.client(okHttpClient)
.build()
}
This is working fine when calling api/products
but I got an error below when using api/type
:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3 path $[0]
The Api service is as below:
interface Service {
@GET("api/type")
suspend fun getType(): Array<Pair<String, Int>>
I understand that the issue is that for one endpoint the response is a JSON and in the other case, it's an array like: [["Model",123],["ModelB",456],["ModelC",789]]
Any idea how to make it works ?
Also is there any addConverterFactory
that allow me to support both json and array at the same time ?
Upvotes: 0
Views: 45
Reputation: 599
Create a class to be used in the data structure:
@JsonAdapter(DataDeserializer::class)
internal data class Data(
val string: String,
val value: Int,
)
Write custom deserializer:
internal class DataDeserializer : JsonDeserializer<Data> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): Data {
val jsonArray = requireNotNull(json?.asJsonArray)
val string = jsonArray[0].asString
val value = jsonArray[1].asInt
return Data(string, value)
}
}
The service will look like this:
internal interface Service {
@GET("api")
suspend fun getData(): Array<Data>
}
Be sure to specify addConverterFactory(GsonConverterFactory.create())
and you can substitute test data for the server by adding an interceptor:
@Singleton
@Provides
fun provideDefaultOkHttpClient(): OkHttpClient =
OkHttpClient
.Builder()
.addInterceptor { chain ->
Response.Builder()
.request(chain.request())
.code(200)
.protocol(Protocol.HTTP_2)
.message("message")
.body("[[\"Model\",123],[\"ModelB\",456],[\"ModelC\",789]]".toResponseBody("application/json".toMediaType()))
.build()
}
.build()
@Singleton
@Provides
fun provideRetrofit(
okHttpClient: OkHttpClient
): Retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://test.com")
.client(okHttpClient)
.build()
Upvotes: 1