将ByteArrayOutputStream转换为Kotlin中的json

Joe*_*oel 0 json jackson kotlin

我正在尝试为2个服务创建一个资源,1个在application/x-www-form-urlencoded和string payload中,另一个应用程序/ json格式用json body.

我有这个代码:

@POST @Path("/test")
fun test(@Context request: ContainerRequest): Response {
    val baos = ByteArrayOutputStream()
    request.entityStream.use { it.copyTo(baos) }
    val ipnRawData = baos.toString()
    var map : Map<String,Any>
    map = when (request.headers.getFirst("Content-Type")) {
        "application/json" -> objectMapper.convertValue(ipnRawData,Map::class.java) as Map<String,Any>
        "application/x-www-form-urlencoded" -> LinkedHashMap()
        else -> throw UnsupportedOperationException()
    }
    //....handle the map
    return Response.status(200).build()
}
Run Code Online (Sandbox Code Playgroud)

但是当我尝试使用json选项和body :)运行它时{"name" :"test"},我收到一个错误:

"java.lang.IllegalArgumentException:无法构造java.util.LinkedHashMap的实例:no String-argument构造函数/工厂方法从String值反序列化('{"name":"test"}')"

谢谢你的帮助,Yoel

Jay*_*ard 5

您应该使用mapper.readValue将JSON反序列化为对象.

使用原始杰克逊没有杰克逊科特林模块:

val map: Map<String, String> = JSON.readValue("""{"name" :"test"}""",
                      object : TypeReference<Map<String, String>>() {})
Run Code Online (Sandbox Code Playgroud)

这传递了一个带有超类的对象表达式,TypeReference指定了您想要创建的类型,完整的泛型仍然完整(您的方法遭受类型擦除).

相反,如果您使用的Jackson-Kotlin模块,您只需要:

val map: Map<String, String> = JSON.readValue("""{"name" :"test"}""")
Run Code Online (Sandbox Code Playgroud)

因为它有辅助/扩展功能来隐藏一些像TypeReference创建这样的丑陋的东西.

您应该始终将Jackson-Kotlin模块与Kotlin代码一起使用,以便您可以实例化任何类型的Kotlin对象,包括具有所有val参数且没有默认构造函数的数据类,使其理解可为空性,还可以处理构造函数参数的默认值.一个简单的独立示例:

import com.fasterxml.jackson.module.kotlin.*

val JSON = jacksonObjectMapper() // creates ObjectMapper() and adds Kotlin module in one step

val map: Map<String, String> = JSON.readValue("""{"name" :"test"}""")
Run Code Online (Sandbox Code Playgroud)

注意导入,.*以便它获取所有扩展函数,否则您需要显式导入: com.fasterxml.jackson.module.kotlin.readValue

或者在您的情况下,修改后的代码将是:

import com.fasterxml.jackson.module.kotlin.readValue

val objectMapper = jacksonObjectMappe() // instead of ObjectMapper()

... 

@POST @Path("/test")
fun test(@Context request: ContainerRequest): Response {
    val bodyAsString = request.entityStream.bufferedReader().readText() 
    val map: Map<String, Any> = when (request.headers.getFirst("Content-Type")) {
        "application/json" -> objectMapper.readValue(bodyAsString) 
        "application/x-www-form-urlencoded" -> LinkedHashMap()
        else -> throw UnsupportedOperationException()
    }
    //....handle the map
    return Response.status(200).build()
}
Run Code Online (Sandbox Code Playgroud)

代码也被清理了一点,以删除a的使用,var并以更Kotlin友好的方式读取实体流.

另请注意,Content-Type标头可能更复杂,它也可能包含编码,例如:

Content-type: application/json; charset=utf-8
Run Code Online (Sandbox Code Playgroud)

因此,您可能需要一个实用程序函数来检查标头是"等于application/json还是以application/json;" 开头,而不是仅检查相等性.

最后,您可以request.entityStream直接传递给objectMapper.readValue它,并且永远不会将它复制到字符串中.有各种重载readValue可以帮助这些类型的输入.