How to use ktor ContentNegotiation for json deserialising for websocket client

86 Views Asked by At

I am investigating ktor client in my current android project for consuming a websocket stream.

my ktor client is configured as follows:-

val client = HttpClient(OkHttp) {
    install(Logging) {
        logger = Logger.DEFAULT
        level = LogLevel.ALL
    }

    install(ContentNegotiation) {
        json(Json {
            ignoreUnknownKeys = false
            isLenient = true
            encodeDefaults = true
            coerceInputValues = true
            explicitNulls = false
        })
    }

    install(WebSockets) {
        pingInterval = 20_000

    }
}

with my websocket session configured as shown below:-

    client.webSocket(urlString = "wss://stream.org") {
        incoming.consumeEach { frame ->
            when (frame) {
                is Frame.Binary -> println("Frame.Binary = $frame")
                is Frame.Text -> {
                    println("Frame.Text = $frame")
                    val incoming = frame.readText()
                    println("\tincoming $incoming")

                    val eventResponse = json.decodeFromString<WebSocketResponse>(incoming)

                    when (eventResponse.event) {
                        Event.CONNECTED  -> println("Event.SUBSCRIPTION_CREATED = $incoming")
                        Event.SUBSCRIPTION_CREATED -> println("Event.SUBSCRIPTION_CREATED = $incoming")
                        Event.SUBSCRIPTION_DELETED -> println("Event.SUBSCRIPTION_DELETED = $incoming")
                        Event.TOPIC_ADDED -> println("Event.TOPIC_ADDED = $incoming")
                        Event.TOPIC_REMOVED -> println("Event.TOPIC_REMOVED = $incoming")
                        Event.TOPIC_UPDATED -> println("Event.TOPIC_UPDATED = $incoming")
                        else -> TODO("Unexpected event $eventResponse")
                    }
                }

                is Frame.Close -> println("Frame.Close = $frame")
                is Frame.Ping -> println("Frame.Ping = $frame")
                is Frame.Pong -> println("Frame.Pong = $frame")
                else -> TODO("Unexpected frame = $frame")
            }
        }
    }

the json in val eventResponse = json.decodeFromString<WebSocketResponse>(incoming) is a different json instance than the one configured in the ktor client, as i have it already declared for processing restful api calls via retrofit

its declared as follows:-

@OptIn(ExperimentalSerializationApi::class)
val json = Json {
    ignoreUnknownKeys = true
    isLenient = true
    encodeDefaults = true
    coerceInputValues = false
    explicitNulls = false
}

how do i use the json instance i configured for content negotiation?

1

There are 1 best solutions below

0
Aleksei Tirman On

You cannot access the Json instance from the ContentNegotiation plugin, but you can configure the plugin with the predefined Json object and use it for the WebSockets packet deserialization too.

@OptIn(ExperimentalSerializationApi::class)
val jsonInstance = Json {
    ignoreUnknownKeys = true
    isLenient = true
    encodeDefaults = true
    coerceInputValues = false
    explicitNulls = false
}

val client = HttpClient {
    install(ContentNegotiation) {
        json(jsonInstance)
    }
}

// ... 
// Usage of jsonInstance for WebSockets packet deserialization