Home > Net >  Kotlinx.Serialization using OkHTTPClient return always Failure
Kotlinx.Serialization using OkHTTPClient return always Failure

Time:09-24

Hello I have a problem with my JSON. I am using OkHTTPClient to get JSON from web - to get objects from JSON using kotlinx.serialization via method which contains this and return value from method should be Result :

private suspend inline fun <reified T> OkHttpClient.get(webUrl: HttpUrl): Result<T> =
    try {
        //Builder defined here ... but skipping this line of code
        val data = Json { ignoreUnknownKeys = true }.decodeFromString<T (result.body!!.string())
        Result.Success(data)
    } catch (e: Exception) {
        Result.Failure(e)
    }


 suspend fun getFact(): Result<Fact> =
        client.httpGet("myURL".toHttpUrl())

Json from myURL:

{"status":"success","data":[{"fact":"This is random information i need to get"}],"message":"Retrieved Fact"} 

My serializer and Serializable data classes:


@Serializable
data class Fact(
@Serializable(with = FactListSerializer::class)
val data: String) java.io.Serializable

object FactListSerializer : JsonTransformingSerializer<List<String>>(ListSerializer(String.serializer())) {
    override fun transformDeserialize(element: JsonElement): JsonElement {
        return if (element  is JsonArray) {
            JsonArray(listOf(element)).first()
        } else {
            element
        }
    }
}

To be honest I am not sure what I am doing, but I am getting this error all the time when I print val fact = api.getFact():

 Fact: Failure(error=kotlinx.serialization.json.internal.JsonDecodingException: Expected JsonPrimitive at 0, found {"fact":"This is random information i need to get"}

What I need to return is only first element of array fact, because JSON obtain always only 1 fact inside array. So I don't want to return from Serializer/Json List but only Fact object. But as you see I am obtaining always Result Fauilure, don't know why. My goal is to obtain Result Success and obtaining from that JSON object Fact (only one), but I am not sure if I am doing it correct (obviously not) and even if it is even possible to return from JSONArray only one object (element of type Fact).

So what I expect is something like this:

Fact: Success(value=Fact(fact=This is random information i need to get))

CodePudding user response:

I think the deserializer definition should be changed on 3 levels. The example of how to use JsonTransformingDeserializer in the docs actually describes most of what you need.

  1. JsonArray(listOf(element)).first() should just be element.first(). Here you're building a JsonArray containing your initial JsonArray as only element, and then taking the first, so you basically get back the exact same element.

  2. The type parameter T of JsonTransformingSerializer is supposed to be the type of the property it's applied to, so you should at least get a warning in the code because yours is defined to work on List<String> but is applied to a String property. It should be JsonTransformingSerializer<String>(String.serializer()).

  3. You not only need to unwrap the data array, you also need to extract the value of the fact key within the element of that array.

So with all these changes, it should give something like this:

object FactListSerializer : JsonTransformingSerializer<String>(String.serializer()) {
    override fun transformDeserialize(element: JsonElement): JsonElement {
        val unwrappedData = if (element is JsonArray) element.first() else element
        return unwrappedData.jsonObject["fact"] ?: error("missing 'fact' key in 'data' array")
    }
}
  • Related