简体   繁体   English

将 Map 转换为数据 Class

[英]Convert a Map to a Data Class

Is there an easy method in Kotlin to allow me to convert a Map to an arbitrary data class? Kotlin 中是否有简单的方法允许我将 Map 转换为任意数据 class?

The Map I'm trying to convert has all of the keys of the required fields of the data class.我正在尝试转换的 Map 具有数据 class 所需字段的所有键。

I've hunted around, but haven't been able to find anything that seems to talk about doing this in a very generic way.我四处寻找,但没能找到任何似乎以非常通用的方式谈论这样做的东西。

I know that I can use ObjectMapper , but that requires an extra library.我知道我可以使用ObjectMapper ,但这需要一个额外的库。 Looking for something that is available just with Kotlin.寻找 Kotlin 可用的东西。

Why not just use a map delegate?为什么不只使用地图委托?

class MyData(val map: Map<String, String>) {
    val foo by map
    val bar by map
}

Or you can wrap it using a companion object and call it using MyData.from(mymap)或者您可以使用伴随对象包装它并使用MyData.from(mymap)调用它

data class MyData(val foo: String, val bar: String) {
    companion object {
        fun from(map: Map<String, String>) = object {
            val foo by map
            val bar by map

            val data = MyData(foo, bar)
        }.data
    }

Maybedelegated properties is something for you.也许委托属性适合您。 That way you can also just adapt the map.这样你也可以只调整地图。 That however also allows adding keys and values which aren't 1:1 mappable to your class properties, eg:然而,这也允许添加不能 1:1 映射到您的类属性的键和值,例如:

class YourData { 
  val backedMap = mutableMapOf<String, String>()
  var someProp by backedMap
}

val obj = YourData().apply {
    someProp = "value"
}

Now if backedMap is visible, then the following can also be added, even though there does not exist any such property:现在,如果backedMap可见,则还可以添加以下内容,即使不存在任何此类属性:

obj.backedMap["unknown prop"] = "some other value"

On the other hand however the same map can be used to automatically fill up your fields:另一方面,同样的地图可用于自动填写您的字段:

val sourceMap = mutableMapOf("someProp" to "newValue") // the map you want to use to automatically fill up your data class
sourceMap.forEach { key, newValue -> obj.backedMap[key] = newValue }

Another way is to "just" use reflection.另一种方法是“只是”使用反射。 That way you omit the backing map and rather just scan the properties instead, eg:这样你就可以省略支持映射,而只是扫描属性,例如:

YourData::class.memberProperties.asSequence()
                                .filterIsInstance<KMutableProperty<*>>()
                                .filter { it.name in sourceMap.keys }
                                .forEach { prop ->
                                  prop.setter.call(obj, sourceMap[prop.name])
                                }

using kotlin reflection to map constructor parameters to map values this generic function will work for any class that receives field data in the primary constructor使用 kotlin 反射将构造函数参数映射到映射值,此通用函数适用于在主构造函数中接收字段数据的任何类

Works well with JSON dbs like firebase and was tested with Map objects from Firebase DocumentSnapshot.data与 Firebase 等 JSON 数据库配合良好,并使用 Firebase DocumentSnapshot.data 中的 Map 对象进行了测试

fun <T : Any> mapToObject(map: Map<String, Any>, clazz: KClass<T>) : T {
    //Get default constructor
    val constructor = clazz.constructors.first()
    
    //Map constructor parameters to map values
    val args = constructor
    .parameters
    .map { it to map.get(it.name) }
    .toMap()
   
    //return object from constructor call
    return constructor.callBy(args)
}

with some class有一些课

data class Person(val firs: String, val last: String)

use like this像这样使用

mapToObject(map, Person::class)

Though this is coming late, but it may help someone like it just helped me.虽然这来晚了,但它可能会帮助像它这样的人帮助我。

I reviewed @killjoy 's comment and immediately I was mind struck that my honest answer had being on my lap since.我查看了@killjoy 的评论,我立即感到震惊,因为我的诚实回答一直在我的腿上。

Answer: Using any Json libary eg GSON() or JSon Kotlin serilization Libary, this problem can be solved.答:使用任何 Json 库,例如 GSON() 或 JSon Kotlin 序列化库,都可以解决这个问题。

This is a generic example:这是一个通用示例:

    allUploadsLiveData.observe(viewLifecycleOwner, {
        val userUploads = ((it.value as HashMap<*, *>).values.toList())
        for (upload in userUploads){
            val product = ((upload as HashMap<*, *>).values.toList()[0] as HashMap<*, *>)
            
            // Use Gson() to convert to Json format 
            val json = Gson().toJson(product)
            try {
                //Use Gson() to convert to Your custom class. For me here is ProductData
                val data = Gson().fromJson(json, ProductData::class.java)
                println("This is all the new data *********************************** ${data.productImageUrl}")
            } catch (e: Exception){
                println("Exception *************** $${e.printStackTrace()}")
            }
        }
    })

Don't forget to add Gson() dependency version 2.9.0 is the most resent version at this time of my response.不要忘记添加 Gson() 依赖项版本 2.9.0 是我此时回复的最新版本。 Replace the version with the most rest from Google Gson替换谷歌最多的版本 rest Gson

implementation "com.google.code.gson:gson:2.8.8"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM