将字段名称和值的映射反序列化为基于KClass的数据类反射

问题描述

我想从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 }}。