解决Kotlin中Json序列化框架无法解析data class泛型的问题

原创
2023/09/28 12:19
阅读数 622

先说结果

换了Solon框架后 all in kt 遇到了一个问题。就是json数据层级深的时候只能序列化第一层,经过测试主流的json框架都是这样。 研究了一天终于找到一个不依赖的解决方法。先上结果代码:

@OptIn(ExperimentalStdlibApi::class)
fun main(args: Array<String>) {
    val ret = """{"code":200,"msg":"成功","data":{"id":"out123","name":"outName","inner":{"id":"inner123","name":"innerName","innerdata":["1","2","3"]}}}"""

    // inner解析结果 snack3: false | fastJson2: false | gson: false
    val snack3 = ONode.deserialize<Ret<Out>>(ret,Ret<Out>()::class.java)
    val fastJson2 = JSON.parseObject<Ret<Out>>(ret, Ret<Out>()::class.java)
    val gson = Gson().fromJson<Ret<Out>>(ret, Ret<Out>()::class.java)

    // inner解析结果 snack3: true | fastJson2: true | gson: true
    val typeOf_T = typeOf<Ret<Out>>().javaType
    val typeOf_snack3 = ONode.deserialize<Ret<Out>>(ret,typeOf_T)
    val typeOf_fastJson2 = JSON.parseObject<Ret<Out>>(ret, typeOf_T)
    val typeOf_gson = Gson().fromJson<Ret<Out>>(ret, typeOf_T)

}

data class Ret<T>(
    var code:String?=null,
    var msg:String?=null,
    var data:T?=null
)
data class Out(
    var id:String?=null,
    var name:String?=null,
    var inner:Inner?=null
)
data class Inner(
    var id:String?=null,
    var name:String?=null,
    var innerdata:List<String>?=null
)

使用typeOf<T>().javaType 传递给反序列化的Type参数即可 不过使用这个方法得在调用方法上面加上@OptIn(ExperimentalStdlibApi::class)

再说说细节

data class 用起来确实方便,但是由于kt的设计思想,data class 会被编译成 final class 。就是说这个数据类型不能被继承了。 也就导致上述的问题,基本上官方给的使用匿名内部类传递泛型的方法不能使用,就是下面这种:

// 取自snack3文档
List<UserModel> list = ONode.deserialize(json, (new ArrayList<UserModel>(){}).getClass());

kt里得用object:来实现匿名内部类,类似这种

val list: List<UserModel> = ONode.deserialize<List<UserModel>>(json, object : ArrayList<UserModel>() {}.javaClass)

如果使用官方的方法,就得让接收结果的类变成open class 虽然只变最上面那层就可以,但是写起来就有点怪,毕竟有个空的类显示的放在那里。 所以如果想要通用结果类和数据类都是data class 那最好是使用typeOf<T>().javaType 传递Type

还有一个方法

因为最开始没意识到kt里还能直接获取.javaType使用了Solon框架,框架默认使用了Snack3。 所以利用TypeRef和kt的泛型实化原理封装了一个方法:

inline fun <reified T> gType(): Type? {
    return object : TypeRef<T>() {}.type
}

声明成顶层函数即可 使用的时候调用函数就行了:

val ret= ONode.deserialize<Ret<Out>>(ret,gType<Ret<Out>>())

这种方式本质上也是使用了匿名内部类,但内部类声明的地方至少不会出现在代码显示调用的地方。 不过这种方式依赖序列化框架,TypeRef是Snack3提供的,GSON里好像叫TypeToken。 喜欢这种方式的也可以自行封装一下。

展开阅读全文
加载中
点击加入讨论🔥(1) 发布并加入讨论🔥
打赏
1 评论
0 收藏
0
分享
返回顶部
顶部