Использование универсальной функции не работает для загрузки списка YAML с Джексоном - PullRequest
1 голос
/ 25 июня 2019

Я только недавно начал испытывать Kotlin, и я до сих пор поражен этим.Тем не менее, я не могу полностью понять, как здесь работают дженерики.

Я хочу прочитать файл YAML, который выглядит следующим образом:

- id: acrobatics
  name: Akrobatik
  description:
  ...

Простой класс данных для сопоставленияэто к:

data class Skill (
    val id: String,
    val name: String,
    val description: String
)

Теперь перейдем к интересной части. По-видимому, он работает с генериками! Я начал с этой функции, которая выполняет свою работу, как задумано:

fun loadSkills(): List<Skill> {

    val resource: URL = classLoader.getResource("rulebook/skills.yml")!!

    val items: List<Skill> = resource.openStream()
        .bufferedReader().use() { reader ->
            objectMapper.readValue<List<Skill>>(reader)
        }

    return items
}

Однако я хочу использовать это повторно, поэтому я попытался создать универсальныйfunction:

protected fun <R: Any> loadList(path: String): List<R> {

    val resource: URL = classLoader.getResource(path)!!

    val items: List<R> = resource.openStream()
        .bufferedReader().use() { reader ->
            objectMapper.readValue<List<R>>(reader)
        }

    return items
}

А моя первоначальная функция просто вызывает:

return loadList<Skill>("rulebook/skills.yml")

А теперь мои тесты не пройдены, потому что я получаю список LinkedHashMaps, который, по-видимому, является типом данных по умолчанию, используемымПарсер YAML для отображения объектов.Я прочитал немного больше и попытался изменить подпись моего метода на встроенную функцию с параметром reified типа:

protected inline fun <reified R: Any> loadList(path: String): List<R>

Но это, похоже, ничего не меняет.Есть ли способ сделать это элегантно?

1 Ответ

1 голос
/ 25 июня 2019

Поскольку вся информация общего типа не существует во время выполнения, вам нужно как-то предоставить ее Джексону. Reified type хранит эту информацию, но AFAIK Джексон не использует ее, поэтому вы должны сделать это вручную. Например, вот так:

inline fun <reified T> loadSkills(path: String): List<T> {

    val resource: URL = javaClass.classLoader.getResource(path)
    val type = objectMapper.typeFactory.constructParametricType(List::class.java, T::class.java)
    val items: List<T> = resource.openStream()
                .bufferedReader().use { reader ->
                    objectMapper.readValue(reader, type)
                }

    return items
}

РЕДАКТИРОВАТЬ : Оказывается, есть ограничение в модуле Джексона-Котлина для автоматического вычисления вложенных универсальных типов. Если вы попытаетесь проанализировать yaml (или что-то еще) для отдельного навыка (без списка), он будет работать правильно даже внутри универсальной функции, но как только вы захотите иметь вложение типа List , он не сможет правильно распознать тип и выдаст List of HashMaps

...