【问题标题】:Convert ByteArrayOutputStream to json in Kotlin在 Kotlin 中将 ByteArrayOutputStream 转换为 json
【发布时间】:2016-09-14 15:41:44
【问题描述】:

我正在尝试为 2 个服务创建一个资源,其中 1 个是 application/x-www-form-urlencoded 和字符串有效负载,另一个是带有 json body 的 application/json 格式。

我有这个代码:

@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 选项和正文:{"name" :"test"}) 运行它时,我得到一个错误:

"java.lang.IllegalArgumentException:无法构造 java.util.LinkedHashMap 的实例:没有字符串参数构造函数/工厂方法可以从字符串值反序列化('{ "name" :"test"}')"

感谢您的帮助,Yoel

【问题讨论】:

    标签: json jackson kotlin


    【解决方案1】:

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

    使用原始 Jackson 没有 Jackson-Kotlin module

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

    这会传入一个带有超类TypeReferenceobject expression,指定您要创建的类型,而完整的泛型仍然完好无损(您的方法会遭受类型擦除)。

    相反,如果您正在使用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; 开头”,而不仅仅是一个相等性检查。

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

    【讨论】:

    • 感谢您的详细回答和有用的提示!
    猜你喜欢
    • 2015-01-13
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 2019-10-24
    • 2022-01-22
    • 1970-01-01
    • 2021-10-15
    相关资源
    最近更新 更多