使用通用函数无法通过Jackson加载YAML列表

时间:2019-06-25 10:57:11

标签: generics kotlin jackson yaml

我最近才开始尝试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
}

但是,我想重用它,所以我尝试创建一个通用函数:

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解析器用来映射对象的默认数据类型。 我读了一些,并尝试将我的方法签名更改为带有类型化参数的内联函数:

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

但这似乎并没有改变任何东西。 有什么方法可以使它优雅地工作吗?

1 个答案:

答案 0 :(得分:1)

由于在运行时不存在所有通用类型信息,因此您需要以某种方式将其提供给Jackson。修饰类型保留该信息,但是AFAIK Jackson不使用它,因此您必须手动进行操作。例如,像这样:

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
}

编辑:事实证明,jackson-kotlin-module中有一个自动计算嵌套泛型类型的限制。 如果您尝试解析单个技能(不带列表)的yaml(或其他),即使在通用函数中也可以正常工作,但是一旦您想要像List 这样的嵌套,它就无法正确识别类型并生成List of HashMaps