Typhaon
Typhaon

Reputation: 1054

Receive Map in ktor server using kotlinx.serialization

I have a Ktor server running with kotlinx.serialization as it's json (de)serializer I send my Ktor a message like this one:

{
  "Brick(partNumber=004229, partName=Sticker Sheet for Set 295-1, dataSource=Rebrickable, brickImage=null, categoryID=58)": 5
}

which is a pair of an Int and this class:

import kotlinx.serialization.Serializable

@Serializable
data class Brick(
    val partNumber: String,
    val partName: String,
    val dataSource: String,
    val brickImage: String?,
    val categoryID: Int?
)

But I get this error

kotlinx.serialization.json.JsonDecodingException: Invalid JSON at 0: Expected '[, kind: MAP'
    at kotlinx.serialization.json.internal.JsonReader.fail(JsonReader.kt:293)

Which to me means that kotlinx.serialization expects a different syntax for the map class. Which is odd to me. When I change the type into List> it throws the same exception but with LIST instead of MAP. EDIT: Upon further inspection, it expects a [ instead of a { at the start of the line. My (partial) application implementation

fun Application.module(testing: Boolean = false) {
    install(ContentNegotiation) { serialization() }
    routing {
        route("user") {
            route("brick") {
                post {
                    call.request.queryParameters["userName"]
                        ?.let { userRepository.login(it) } // Someone else is still working login nvm this
                        ?.let { user ->
                            val bricks = call.receive<Map<Brick, Int>>() // This throws an error
                            userRepository.addBricks(user, bricks)
                            call.respond(HttpStatusCode.OK)
                        }
                        ?: call.respond(HttpStatusCode.Unauthorized)
                }
            }
        }
    }
}

The android retrofit function that sends the class (using GSON):

    @POST("/user/brick")
    suspend fun setBricksAmounts(
        @Query("userName")
        userName: String,
        @Body
        brickAmounts: Map<Brick, Int>
    )

Upvotes: 1

Views: 2041

Answers (1)

Nikky
Nikky

Reputation: 518

I think using a class as a key does not work in kotlinx serialization and it looks like that class is just serialized into a string to use it as key

Instead you can receive it as Map<String, Int> and afterwards run

bricks.mapKeys { (jsonString, number) ->
   Json(JsonConfiguration.Stable).parse(Brick.Serializer, jsonString)
}

or the equivalent jackson code if you want

Upvotes: 2

Related Questions