com.google.gson.internal.LinkedTreeMap无法转换为我的自定义类

时间:2019-09-26 17:04:11

标签: kotlin gson

我得到:

com.google.gson.internal.LinkedTreeMap cannot be cast to Message class

我想将任何类型的对象转换为我的自定义类。我正在尝试gson将对象转换为JSON,然后将JSON字符串转换为我的Message类。

我使用Gson创建了具有通用类型转换的函数。

//Custom Class
data class Message(
    @SerializedName("content")
    val content: String? = null
)

//My abstract class

abstract class EchoListener<T> {
    companion object {
        private val TAG = EchoListener::class.java.simpleName
    }

    abstract fun onDataReceived(data: T?)

    fun submitData(it: Any) {
        Log.e(TAG, "Echio data  ${it}")
        val gson = Gson()
        val json = gson.toJson(it)
        val data: EchoData<T> = gson.fromJson(json, genericType<EchoData<T>>())
        Log.e(TAG, "Converted data ${data.data}")
        onDataReceived(data.data)
    }
}

inline fun <reified T> genericType() = object : TypeToken<T>() {}.type


class SomeClass {
    fun <T> listen(event: String, callback: EchoListener<T>) {
        val listener = Emitter.Listener {
            Log.e(TAG, "Data ${it[1]}")
            if (it.size > 1)
                callback.submitData(it[1])
        }
        this.socket.on(event, listener)
        this.bind(event, listener)
    }
}


//Main Activity
someClassObj?.listen("chat", object : EchoListener<Message>() {
    override fun onDataReceived(data: Message?) {
        Log.e(TAG, "Message ${data}")
    }
})

已编辑

以下是我正在创建的库的链接: Github repo

这是示例Android应用程序 Android Github Repo

2 个答案:

答案 0 :(得分:1)

可以使用kotlinx.serialization以一种非常简单的方式完成此操作。首先,请参见exmple了解其工作原理,然后将库依赖项添加到应用程序build.gradle:

implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0"

请注意,要能够在Kotlin代码中使用Kotlin序列化库,请确保您的Kotlin编译器版本为1.3.30或更高版本。

现在,您需要使用@Serializable注释注释要序列化/反序列化的所有类,并确保要从kotlinx.serialization.Serializable进行导入。

在为类加上注释后,编译器将为每个类自动以Java字节代码serializer()生成一个静态函数,我们将调用该函数对该类进行序列化和反序列化。

现在要实现通用的序列化/反序列化功能,请将此文件添加到您的项目中:

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.json.Json

object SerializationManager {

    fun <T> deserializeObject(
        json: String,
        deserializationStrategy: DeserializationStrategy<T>
    ): T = Json.parse(deserializationStrategy, json)


    fun <T> getList(json: String, dataSerializer: KSerializer<List<T>>): List<T> =
        Json.parse(dataSerializer, json)



    fun <T> serializeObject(data: T, serializationStrategy: SerializationStrategy<T>): String =
        Json.stringify(serializationStrategy, data)


    fun <T> serializeList(
        data: List<T>,
        dataSerializer: KSerializer<List<T>>
    ): String = Json.stringify(dataSerializer, data)

}

如何使用SerializationManager的示例

我有一个要序列化的Reciter类:

import kotlinx.serialization.Serializable

@Serializable
data class Reciter(
    val id: String,
    val name: String,
    val servers: String
)

然后在您的Activity或其他位置:

 val reciter = Reciter("A123", "ABCD", "https://stackoverflow.com")
 val json1 = SerializationManager.serializeObject(reciter, Reciter.serializer())


 // If we want to serialize a list of reciters, then we pass Reciter.serializer().list
 val recitersList = listOf(
     Reciter("A123", "ABCD", "https://stackoverflow.com"),
     Reciter("B456", "F.abc", "https://stackoverflow.com"),
     Reciter("M568", "NDK", "https://stackoverflow.com")
 )
 val json2 = SerializationManager.serializeList(recitersList,Reciter.serializer().list)

答案 1 :(得分:1)

您尝试以非常复杂的方式使用它。使用Gson可以轻松得多。

这里有四种扩展方法,几乎​​可以满足您所需的所有情况。如果没有,我希望其余的人会根据这些发现。

inline fun <reified T> T.toJson(): String = Gson().toJson(this)

inline fun <reified T> String.toObject(): T = Gson().fromJson(this, T::class.java)

inline fun <reified T, K, V> Map<K, V>.toObject(): T = Gson().fromJson(this.toJson(), T::class.java)

// and more classic but less used
fun <T> String.toObject(type: Type): T = Gson().fromJson(this, type)

现在您可以像使用它了

data class SomeClass(val today: Int, val yesterday: Int)

val someClass = SomeClass(3,4)
val json = someClass.toJson() // { "today" : 3,"yesterday" : 4 }
val someClassDesetialized = json.toObject<SomeClass>() // SomeClass(3,4)

val map: Map<String, Int> = mapOf("today" to 2,"yesterday" to 1)
val mapJson= map.toJson() // { "today" : 2,"yesterday" : 1 }
val someClassFromMap = map.toObject<SomeClass, String, Int>() // SomeClass(2,1)
val someClassFromMapJson: SomeClass = mapJson.toObject<SomeClass>().copy(5) // SomeClass(5,1)

...

val data: EchoData<T> = json.toObject()
val data1 = json.toObject<EchoData<T>>()

类似的东西。请注意,Gson lib使用Java serialization,因此它可能比Kotlin特定的kotlinx.serialization慢且安全性低,但是它没有像kotlinx.serialization那样处于prealpha版本,因此它更不容易出错且更成熟。 >

Edit1

这是如何以行业标准的方式隐藏转换的方法。

inline fun <reified T> Map<String, Any>.convert(): T = toObject() 

然后像使用它

map.convert<SomeClass>()

它比传递Type as方法参数更好。

您将不得不调整此方法以从Java代码中使用。 Here说明了操作方法。

Edit2

这是您的应用已解决的体系结构问题

您示例中的SomeClass应该是

class SomeClass {
        inline fun <reified T> listen(event: String, callback: (T) -> Unit) {
            Emitter.Listener {
                Log.e(TAG, "Data ${it[1]}")
                if (it.size > 1)
// I don't know what it[1] is so I assume it is map<String, Any> since it was in an original issue.
                    callback((it[1] as Map<String, Any>).toObject<T, String, Any>())
            }.apply{
                this@SomeClass.socket.on(event, this)
                this@SomeClass.bind(event, this)
            }
        }
    }

现在可以将其使用量减少到

 SomeClass().listen<Message>("someEvent", { message ->
// do some stuff with message
            })

这不是从Map到Pojo的原始转换问题的解决方案。这是有缺陷的方法的体系结构修复。

希望有帮助。