问题描述
我想从KClass实例和Map
一个例子是:
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并将其反序列化,但这并不是我所希望的。我想用反射构建自己的版本。
解决方法
实际上,并非所有序列化库都使用反射。例如,kotlinx.serialization中的魔术基于编译器插件生成的其他字节码。通常,它比运行时对Reflection API的调用要快,而且我不确定在这种情况下“简单版本”是否与“性能更高”相同。
如果您仍然想发明轮子,可以通过反射来完成:
p.exif
=> "{\"exposure_time\":\"1/4\",\"fnumber\":\"8/5\",\"exposure_program\":2,\"iso_speed_ratings\":1250,\"exif_version\":\"0231\",\"date_time_original\":\"2020:10:04 21:42:05\",\"components_configuration\":\"\\u0001\\u0002\\u0003\\u0000\",\"shutter_speed_value\":\"61506/32255\",\"aperture_value\":\"14447/10653\",\"brightness_value\":\"-42954/7669\",\"exposure_bias_value\":\"-15/256\",\"metering_mode\":5,\"flash\":16,\"focal_length\":\"21/5\",\"subject_area\":0,\"color_space\":65535,\"pixel_x_dimension\":4032,\"pixel_y_dimension\":3024,\"sensing_method\":2,\"scene_type\":\"\\u0001\",\"custom_rendered\":8,\"exposure_mode\":0,\"white_balance\":0,\"scene_capture_type\":0,\"flash_pix_version\":\"0100\"}"
p.exif.class
=> String
如果使用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)
}
类型参数,则可以稍微简化API:
reified
请注意,此代码段不提供任何类型转换,并且如果inline fun <reified T : Any> deserialize(properties: Map<String,*>): T {
val clazz: KClass<T> = T::class
//other code is the same
}
中有任何非String参数(并且T
被传递给{{1 }}。