为什么这个调用.receive< T>()将json数组项完全转换为Ktor中的字符串类型?

dldeef67  于 5个月前  发布在  其他
关注(0)|答案(1)|浏览(67)

在Ktor设置上下文中考虑下面的示例化:

  • 一个简单的模型来解析JSON请求(旨在只接收/存储字符串):
@Serializable
data class TestingDTO(val texts: List<String>)

字符串

  • Ktor入口点:
fun main() {
  embeddedServer(
    Netty,
    port = 8080,
    host = "0.0.0.0",
    module = Application::myApp
  ).start(wait = true)
}

fun Application.myApp() {
  install(ContentNegotiation) {
    json()
  }

  routing {
    post("/testing") {
      runCatching {
        val receivedTestingDTO = call.receive<TestingDTO>()
        println(receivedTestingDTO)
        call.respond(HttpStatusCode.Created)
      }.onFailure {
        call.respond(HttpStatusCode.BadRequest, it.message ?: "deserialization problem!!!")
      }
    }
  }
}

  • 对上述设置的POST请求示例:
curl -v -d '{"texts": ["foo", "bar", 1]}' -H 'Content-Type: application/json' http://127.0.0.1:8080/testing

  • 注意:注意JSON数组同时包含字符串和数字。*

现在,如果我们运行它,基本上call.receive<TestingDTO>()将请求的JSON“完美地”去序列化为TestingDTO类型,即使我们在JSON数组中放入一个数字,也会导致所有项目从该数组转换为String(为了将它们放入目标texts列表中?)。
但是,如果我们使用其他KotlinAPI作为实现方法,例如kotlinx.serialization.json.Json类,则示例请求会如预期的那样失败。例如,runCatching块:

runCatching {
        // now consuming request to read raw json as "text"
        // and using kotlinx class to deserialize that text fails
        // if in request exists a diferent type that doesn't fits the target
        // TestingDTO List
        val receivedTestingDTO = Json.decodeFromString<TestingDTO>(call.receiveText())
        receivedTestingDTO.texts.forEach {
          println("the value [$it] is instance of ${it::class}")
        }
        call.respond(HttpStatusCode.Created)
      }.onFailure {
        call.respond(HttpStatusCode.BadRequest, it.message ?: "deserialization problem!!!")
      }
    }


那么,这里到底发生了什么?为什么会有这两种不同的行为?我是否错过了关于call.receive<>()函数规范的一些东西?
如果没有,“工作”方法值得使用吗?

ldxq2e6h

ldxq2e6h1#

此行为是isLenient json配置构建器的结果。
在Ktor中,在安装ContentNegotiation时用于所有请求的默认JSON配置是:

public val DefaultJson: Json = Json {
    encodeDefaults = true
    isLenient = true
    allowSpecialFloatingPointValues = true
    allowStructuredMapKeys = true
    prettyPrint = false
    useArrayPolymorphism = false
}

字符串
在您的示例中,您使用了Json.decodeFromString而没有配置。默认情况下,isLenient为false。
你应该这样做

val codec = Json { 
    isLenient = true
}
val receivedTestingDTO = codec.decodeFromString<TestingDTO>(call.receiveText())


和Ktor有同样的行为
注意事项:
你可以通过在json函数中提供你自己的构建器来强制一个json配置。

install(ContentNegotiation) {
    json(Json {
        prettyPrint = true
        isLenient = true
    })
}

相关问题