简体   繁体   中英

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?

The Map I'm trying to convert has all of the keys of the required fields of the data 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. Looking for something that is available just with 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)

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:

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:

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

Works well with JSON dbs like firebase and was tested with Map objects from Firebase DocumentSnapshot.data

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.

Answer: Using any Json libary eg GSON() or JSon Kotlin serilization Libary, this problem can be solved.

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. Replace the version with the most rest from Google Gson

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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