Home > Software design >  Kotlin Parsing json array with new line separator
Kotlin Parsing json array with new line separator

Time:07-21

I'm using OKHttpClient in a Kotlin app to post a file to an API that gets processed. While the process is running the API is sending back messages to keep the connection alive until the result has been completed. So I'm receiving the following (this is what is printed out to the console using println())

{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"DONE","transcript":"Hello, world.","error":null}

Which I believe is being separated by a new line character, not a comma.

I figured out how to extract the data by doing the following but is there a more technically correct way to transform this? I got it working with this but it seems error-prone to me.


data class Status (status : String?, transcript : String?, error : String?)

val myClient = OkHttpClient ().newBuilder ().build ()
val myBody = MultipartBody.Builder ().build () // plus some stuff
val myRequest = Request.Builder ().url ("localhost:8090").method ("POST", myBody).build ()

val myResponse = myClient.newCall (myRequest).execute ()
val myString = myResponse.body?.string ()

val myJsonString = "[${myString!!.replace ("}", "},")}]".replace (",]", "]")
// Forces the response from "{key:value}{key:value}" 
// into a readable json format "[{key:value},{key:value},{key:value}]"
// but hoping there is a more technically sound way of doing this

val myTranscriptions = gson.fromJson (myJsonString, Array<Status>::class.java)

CodePudding user response:

An alternative to your solution would be to use a JsonReader in lenient mode. This allows parsing JSON which does not strictly comply with the specification, such as in your case multiple top level values. It also makes other aspects of parsing lenient, but maybe that is acceptable for your use case.

You could then use a single JsonReader wrapping the response stream, repeatedly call Gson.fromJson and collect the deserialized objects in a list yourself. For example:

val gson = GsonBuilder().setLenient().create()

val myTranscriptions = myResponse.body!!.use {
    val jsonReader = JsonReader(it.charStream())
    jsonReader.isLenient = true
    val transcriptions = mutableListOf<Status>()
    
    while (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        transcriptions.add(gson.fromJson(jsonReader, Status::class.java))
    }
    transcriptions
}

Though, if the server continously provides status updates until processing is done, then maybe it would make more sense to directly process the parsed status instead of collecting them all in a list before processing them.

  • Related