Как десериализовать JSON в список с помощью Kotlin + Jackson [дубликат]
этот вопрос уже есть ответ здесь:
каков правильный синтаксис для десериализации следующего JSON:
[ {
"id" : "1",
"name" : "Blues"
}, {
"id" : "0",
"name" : "Rock"
} ]
пробовал:
//Works OK
val dtos = mapper.readValue(json, List::class.java)
однако я хочу:
val dtos : List<GenreDTO> = mapper.readValue(json,
List<GenreDTO>::class.java)
выше синтаксис не правильно и дает:only classes are allowed on the left hand side of a class literal
3 ответов
Примечание: ответ от @IRus также правильный, он был изменен в то же время, когда я написал это, чтобы заполнить более подробную информацию.
вы должны использовать модуль Джексона + Котлина или у вас будут другие проблемы десериализации в объекты Kotlin, когда у вас нет конструктора по умолчанию.
ваш первый пример кода:
val dtos = mapper.readValue(json, List::class.java)
возвращает предполагаемый тип List<*>
поскольку вы не указали больше информации о типе, и это на самом деле List<Map<String,Any>>
который на самом деле не "работает нормально", но не вызывает ошибок. Это небезопасно,не набрано.
второй код должен быть:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
val mapper = jacksonObjectMapper()
// ...
val genres: List<GenreDTO> = mapper.readValue(json)
вам больше ничего не нужно в правой части задания, модуль Котлина для Джексона будет олицетворять дженерики и создавать TypeReference
для Джексона внутренне. Обратите внимание на readValue
импорт, вам это нужно или .*
на com.fasterxml.jackson.module.kotlin
пакета имейте функции расширения, которые делают всю магию.
немного другая альтернатива, которая также работает:
val genres = mapper.readValue<List<GenreDTO>>(json)
нет причин не использовать функции расширения и дополнительный модуль для Jackson. Он небольшой и решает другие проблемы, которые потребуют от вас прыгать через обручи, чтобы сделать конструктор по умолчанию или использовать кучу аннотаций. С модулем ваш класс может быть обычным Kotlin (необязательно быть data
класс):
class GenreDTO(val id: Int, val name: String)
следующий код хорошо работает для меня:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
val json = """[ {
"id" : "1",
"name" : "Blues"
}, {
"id" : "0",
"name" : "Rock"
} ]"""
data class GenreDTO(val id: Int, val name: String)
val mapper = ObjectMapper().registerKotlinModule()
fun main(args: Array<String>) {
val obj: List<GenreDTO> = mapper.readValue(json)
obj.forEach {
println(it)
}
}
эта работа из-за функции расширения, определенной внутри jackson-kotlin-module (который использовал овеществленные генерики):
public inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {})
спасибо @JaysonMinard за уведомление меня об этом.
выход:
GenreDTO(id=1, name=Blues)
GenreDTO(id=0, name=Rock)
ошибка, которую вы получаете, заключается в следующем выражении:
List<GenreDTO>::class.java
из-за того, как jvm обрабатывает дженерики, нет отдельного класса для List<GenreDTO>
таким образом, компилятор жалуется. Аналогично в Java следующее не будет компилироваться:
List<GenreDTO>.getClass()
вот пример, который десериализует список правильно:
val value:List<GenreDTO> = mapper.readValue(json, object : TypeReference<List<GenreDTO>>() {})
как отметил @JaysonMinard, вы можете использовать Джексон-модуль-Котлин для упрощения вызова кому:
val genres: List<GenreDTO> = mapper.readValue(json)
// or
val genres = mapper.readValue<List<GenreDTO>>(json)
это возможно из-за reified type parameters
. Рассмотрим глядя на Extensions
чтобы узнать подробности.