Gson解析带泛型或者List的使用记录

8 篇文章 0 订阅
1 篇文章 0 订阅
文章探讨了在Kotlin中使用Gson库解析JSON时处理泛型类型,特别是List对象和自定义泛型类的方法。通过示例展示了fromJson函数的不同用法,包括创建GsonParameterizedType来指定泛型类型。同时,提到了Proguard配置对于保持Gson序列化和反序列化功能的重要性,提供了防止混淆导致问题的规则示例。
摘要由CSDN通过智能技术生成
val gson = GsonBuilder().create()

/**
 * 通过kotlin语法特性创建Type类
 */
abstract class GsonParameterizedType<T> : ParameterizedType
inline fun <reified T> String.fromJson(customType: GsonParameterizedType<T>? = null): T {
    if (T::class.java == String::class.java) {
        return this as T
    }
    return gson.fromJson(this, customType ?: object : TypeToken<T>() {}.type)
}

inline fun <reified T> createGsonParameterizedType(
    crossinline actualTypeArguments: () -> Array<Type>,
): GsonParameterizedType<T> {
    return object : GsonParameterizedType<T>() {
        override fun getActualTypeArguments(): Array<Type> {
            return actualTypeArguments.invoke()
        }

        override fun getRawType(): Type {
            return T::class.java
        }

        override fun getOwnerType() = null
    }
}

inline fun <reified T> String.fromJsonList(): List<T> {
    return fromJson(createGsonParameterizedType<List<T>> { //这里其实省略了List<T>。因此传给fromJson的实体类是List<XX>对象,
            arrayOf(T::class.java)
        })
}

inline fun <reified T> String.fromJsonList2(): List<T> {
    val listType = object : TypeToken<List<T>>() {}.type
    return gson.fromJson(this, listType)
}

//错误!
inline fun <reified T> String.fromJsonList3(): T { //JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 3 path $[0]
    return fromJson(createGsonParameterizedType {
        arrayOf(T::class.java)
    })
}

//错误!
inline fun <reified T> String.fromJsonList4(): T { //JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 3 path $[0]
    val listType = object : TypeToken<List<T>>() {}.type
    return gson.fromJson(this, listType)
}

如上是一些基础函数。

  • 一、获取List对象
    有如下2种和直接调用fromJson()共计3种方式:
//1. 2. fromJsonList 或者 fromJsonList2
val list<List<XXXBean>> = xxxStr.fromJsonList() //fromJsonList2()

//3. 直接写调用
fromJson<List<XXXBean>>(createGsonParameterizedType {
                arrayOf(XXXBean::class.java)
            })
  • 二、获取其他泛型类型
class CommonResponse<T, G> {
    var status = 0
    var data: T? = null
    var data2:G? = null
}
class CommonResponse<T> {
    var status = 0
    var data: T? = null
}

class CommonResponse2 {
    var status = 0
    var data: Any? = null
}

class DataInfo {
    var name: String? = null
}

fun testGsonData() {
    val text = "{\"status\":102,\"data\":{\"name\":\"dog\"}, \"data2\":{\"name\":\"dog2\"}}"
    // IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 22 path $.data
    //val data:CommonResponse<String> = text.fromJson()

    //正确:通过传入Type,明确指定了我们需要解析的泛型是什么。
    val data:CommonResponse<DataInfo, DataInfo>  //这里写的是2个相同泛型,可以不同;一般我们只有1个泛型。下面也只arrayOf1个。
     = text.fromJson(
        createGsonParameterizedType {
            arrayOf(DataInfo::class.java, DataInfo::class.java) //根据你的泛型类型的数组
        })
        //正确
    val data:CommonResponse<DataInfo>
     = text.fromJson(
        createGsonParameterizedType {
            arrayOf(DataInfo::class.java) //根据你的泛型类型的数组
        })

    logd("data $data")
    //LinkedTreeMap  {name:dog} : 因为是Any即Object,gson默认都是处理成基础类型或者List,Map
    val data2 = gson.fromJson<CommonResponse2>(text, CommonResponse2::class.java)

    //错误:不符合预期,这里得到的与data2一样,是LinkedTreeMap  {name:dog}。
    // 虽然我们写了泛型,但是由于这里没有实际意义,编译后被擦除,最终与上述差不多
    val data3 = gson.fromJson<CommonResponse<DataInfo, DataInfo>>(text, CommonResponse::class.java)

}

总结:
本质上,List类型和自定义泛型数据类型,都是一样的。在编译后被擦除。只是我们可能下意识习惯了,自定义类型,不是数组可能更能理解我们比如传入解析方式。
而List也一样。也是一个class类型,内部的T泛型类型,需要我们指定。

另外查看,gson官方文档,配置混淆规则如下,否则混淆编译后报错:
https://github.com/google/gson/issues/2069
https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken

##---------------End: proguard configuration for Gson  ----------
# 如下两条自行考虑追加也进行了保护。一般也添加
-keep class org.json.* {*;}
-keep public class * implements java.lang.reflect.Type

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值