Hey there, I was wondering how to work with a null...
# squarelibraries
s
Hey there, I was wondering how to work with a nullable JSON response in Retrofit. Particularly with the kotlinx.serialization Retrofit-Converter it doesn't work as I expected it to. Details in replies 👇
✅ 1
I'm starting out with a pretty simply baseline: I have an API endpoint that may return 'null' and represent it as such:
Copy code
@Serializable
data class Object(val int: Int)

interface TestApi {
    @GET("test")
    suspend fun get(): Response<Object?>
}
null
is a valid JSON value. However, the request always fails with
Unexpected JSON token at offset 0: Expected start of the object '{', but had 'n' instead at path: $  JSON input: null
. A test that reproduces it, using MockWebServer:
Copy code
@Test
fun test() = runTest {
    val json = Json {  }

    val server = MockWebServer()
    server.start()
    val url = server.url("/")

    val retrofit = Retrofit.Builder()
        .client(OkHttpClient.Builder().build())
        .baseUrl(url)
        .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
        .build()

    val api = retrofit.create(TestApi::class.java)

    // Valid
    server.enqueue(MockResponse().setBody(""" { "int": 42 } """))
    assertThat(api.get().body()).isEqualTo(Object(42))


    // Throws: JSON token at offset 0: Expected start of the object '{', but had 'n' instead at path: $
    server.enqueue(MockResponse().setBody("""null"""))
    assertThat(api.get().body()).isEqualTo(null)
}
Kotlinx.serialization itself is capable of deserializing nulls, but it depends on how it is used:
Copy code
// Valid
assertThat(json.decodeFromString<Object?>("null")).isNull()

// Throws: Unexpected JSON token at offset 0: Expected start of the object '{', but had 'n' instead at path: $
assertThat(json.decodeFromString(Object.serializer(), "null")).isNull()
The Converter seems to be using the second option. Is there anything in particular I need to do to allow parsing null properly?
j
This is not supported at this time. Retrofit is a Java library and has no way of reading that the inner type parameter is nullable
s
Alright, thanks for the answer. I'll use
Response<ResponseBody>
instead and deserialize in a follow-up step.
j
You could also probably do it with a wrapper like
Response<Optional<YourType>>
👀 1