2016-09-14 7 views
0

Я пытаюсь создать ресурс для 2-х сервисов, 1 в application/x-www-form-urlencoded и строковой полезной нагрузке, а другой формат application/json - с помощью json body.Преобразование ByteArrayOutputStream в json в Kotlin

У меня есть этот код:

@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: Can not construct instance of java.util.LinkedHashMap: no String-argument constructor/factory method to deserialize from String value ('{ "name" :"test"}')"

Спасибо за любую помощь , Yoel

ответ

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 с Котлин кодом, так что вы можете создать экземпляр любого типа объекта Котлин, включая классы данных, которые имеют все 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 и прочитать поток сущностей в более Котлин friendl y way.

Также обратите внимание, что заголовок Content-Type может быть более сложным, он может содержать кодирование, а также такие, как:

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

Таким образом, вы можете служебную функцию, которая проверяет, если заголовок «равен application/json или начинается с application/json; "вместо проверки равенства.

Наконец-то вы можете передать request.entityStream прямо на номер objectMapper.readValue и никогда не копировать его в строку.Существуют различные перегрузки для readValue, которые полезны для этих типов входов.

+0

Спасибо за подробный ответ и полезные советы! – Joel

Смежные вопросы