TL; DR ответ:
Вместо
val list: List<ItemNameDto> = mapper.treeToValue(itemNamesNode)
вы должны написать
val collectionType = mapper.typeFactory.constructCollectionType(List::class.java, ItemNameDto::class.java)
val list: List<ItemNameDto> = mapper.readValue(mapper.treeAsTokens(itemNamesNode), collectionType)
Объяснение
Взгляните на фактическую реализацию treeToValue()
:
inline fun <reified T> ObjectMapper.treeToValue(n: TreeNode): T = treeToValue(n, T::class.java)
T
должно быть в вашем случае List<ItemNameDto>
, но поскольку дженерики стираются на JVM (подробнее об этом в этом вопросе), на самом деле это всего лишь List<*>
, поэтому Джексон делает Не знаю, что делать, и преобразует дерево в простую старую карту.
Если вы хотите принудительно задать определенный тип , определение класса недостаточно (подробнее об этом здесь )! К счастью, у Джексона есть несколько удобных функций для решения этой конкретной задачи. constructCollectionType()
создает JavaType
(не JavaClass) с определенным типом по вашему выбору, в этом случае Джексон знает, что делать!
Sidenote: В настоящее время нет способа сделать это без токенизации дерева, см. эту проблему с github.
Чтобы сделать ваш код более читабельным, вы можете ввести функцию расширения:
fun <T : Any> ObjectMapper.constructCollectionType(kClass: KClass<T>): CollectionType? {
return typeFactory.constructCollectionType(List::class.java, kClass.java)
}