将ByteArrayOutputStream转换为Kotlin中的json

时间:2016-09-14 15:41:44

标签: 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()
}

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

  

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

感谢任何帮助,Yoel

1 个答案:

答案 0 :(得分:5)

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

使用原始杰克逊 Jackson-Kotlin module

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

这会传递一个object expression超类TypeReference,指定您想要创建的类型,完整的泛型仍然完整(您的方法遭受类型擦除)。

相反,如果您正在使用 Jackson-Kotlin module,则只需要:

val map: Map<String, String> = JSON.readValue("""{"name" :"test"}""")

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

您应始终将Jackson-Kotlin module与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"}""")

请注意导入.*,以便它获取所有扩展功能,否则您需要显式导入: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()
}

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

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

Content-type: application/json; charset=utf-8

因此,您可能需要一个实用程序函数来检查标题是否等于application/json或以application/json;&#34开头;而不仅仅是一次平等检查。

最后,您可以将request.entityStream直接传递给objectMapper.readValue,但绝不会将其复制到字符串中。 readValue有各种重载对这些类型的输入很有帮助。