1

我想从 KClass 的实例和 Map<String, String> 构造一个对象,在其中我将字段名映射到值。

一个例子是:

val testVal = mapOf("title" to "Foo", "description" to "bar")

data class Article(val title: String, val description)

fun <T> deserialize(clazz: KClass<T>, Map<String,String>) : T?  =  //magic

val article = deserialize(Article::class, testVal) 
// here article should be Article{title: "Foo", description: "Bar"} 

我的问题是如何使用 Kotlin 反射来做到这一点?如何从可用数据构建对象?我认为只要你指出我正确的方向或给出一个想法就足够了。我的意思是肯定有一种方法,因为我认为这就是 json 序列化程序所做的(我只想要一个更简单的版本,功能更少,在不同的数据格式上)。

如果我可以使用只能存储原始数据类型(int、string、boolean)的数据类来完成这项工作,我会很高兴。

我知道我可以使用像 Jackson 这样的东西,然后构建一个 JsonNode 并反序列化它,但这不是我想要的。我想用反射构建我自己的版本。

4

1 回答 1

2

实际上,并不是所有的序列化库都使用反射。例如,kotlinx.serialization中的魔法是基于编译器插件生成的附加字节码。它通常比对反射 API 的运行时调用更快,我不确定在这种情况下“更简单的版本”是否与“更高性能”相同。

如果您仍在发明轮子,这就是通过反射完成的方法:

fun <T : Any> deserialize(clazz: KClass<T>, properties: Map<String, *>): T {
    val primaryConstructor = clazz.primaryConstructor!! //all data classes has it
    val parameters = primaryConstructor.parameters
        .associateWith { properties[it.name] }
        //Parameters with nullable types or default values in constructor may be omited in JSON
        .filterNot { (parameter, value) -> value == null && parameter.isOptional }
        .toMap()
    return primaryConstructor.callBy(parameters)
}

reified如果您使用类型参数,您可以稍微简化 API :

inline fun <reified T : Any> deserialize(properties: Map<String, *>): T {
    val clazz: KClass<T> = T::class
    //other code is the same
}

请注意,此代码片段不提供任何类型转换,如果T(并Map<String, String>传递给properties)中存在任何非字符串参数,则会引发运行时异常。

于 2020-11-02T20:15:30.037 回答