Android 类似GSON的Json与实体类的相互转换库-Kotlin

Android 类似GSON的Json与实体类的相互转换库-Kotlin

  • 前言
  • 目标
  • 代码
    • 注解
    • 类型识别工具
    • 基础类
  • 使用方法
    • 添加依赖
    • 定义实体类继承BaseJsonEntity
    • demo
    • 混淆规则
  • GitHub
  • 更新2020/2/22

前言

在开发中经常遇到需要将云接口查询回来的JSON数据转换到实体类中使用,以前经常一个个实体类手写转换,太糙了,后来写了个java的工具类,现在开始学习Kotlin语言,所以重新写一个Kotlin的版本,了解下kotlin下使用反射和注解有什么不同。

目标

实现一个JSON数据转实体类的工具库,满足以下条件:
1、能够标注JSON中的字段名,实现实体类中对应接受数据的变量名不一定与JSON字段名相同;
2、能够忽略实体类中的某些变量,不进行转换;
3、能够满足JSONObject中的JSONArray转换到List的要求。

代码

注解

首先定义一个注解

/**
 * 变量注解
 *
 * @author D10NG
 * @date on 2019-10-23 15:29
 */
@kotlin.annotation.Target(AnnotationTarget.FIELD)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class DLField (
    // 在JSON中的名字
    val nameInJson: String = ""
)

类型识别工具

/**
 * 类型识别工具
 *
 * @author D10NG
 * @date on 2019-10-23 16:33
 */
object TypeMatch {

    fun isString(clz: Class<*>) : Boolean {
        return clz == String::class.java
    }

    fun isBoolean(clz: Class<*>) : Boolean {
        return clz == Boolean::class.java
    }

    fun isInt(clz: Class<*>) : Boolean {
        return clz == Int::class.java ||
                clz == Integer::class.java ||
                clz == Byte::class.java ||
                clz == Short::class.java
    }

    fun isLong(clz: Class<*>) : Boolean {
        return clz == Long::class.java
    }

    fun isDouble(clz: Class<*>) : Boolean {
        return clz == Double::class.java ||
                clz == Float::class.java
    }

    fun isList(clz: Class<*>) : Boolean {
        return clz == List::class.java
    }

    fun isBaseJsonEntity(clz: Class<*>) : Boolean {
        val sc = clz.superclass ?: return false
        return sc == BaseJsonEntity::class.java
    }
}

基础类

最重要的代码来了

/**
 * 基础类
 *
 * @author D10NG
 * @date on 2019-10-23 15:25
 */
open class BaseJsonEntity : Serializable {

    /**
     * 从JSON中读取数据
     * @param json
     * @param excludeNames 排除的变量
     */
    open fun setFromJson(json: JSONObject, vararg excludeNames: String) {
        val nameList = excludeNames.asList()
        // 获取所有字段,包含父类的
        val fs: MutableList<Field> = mutableListOf()
        var tempClass: Class<in Any>? = this.javaClass
        while (tempClass != null) {
            fs.addAll(tempClass.declaredFields)
            tempClass = tempClass.superclass
        }
        for (f in fs) {
            // 排除不符合的变量
            val annotation = f.getAnnotation(DLField::class.java) ?: continue
            if (nameList.contains(f.name)) continue
            val modifier = Modifier.toString(f.modifiers)
            if (modifier.contains("static") || modifier.contains("final")) continue
            // 提取json中的字段名
            var name = annotation.nameInJson
            if (name.isEmpty()) {
                name = f.name
            }
            // 将private变量改成外部类可读
            f.isAccessible = true
            // 判断变量类型
            when {
                TypeMatch.isString(f.type) -> f.set(this, json.optString(name))
                TypeMatch.isInt(f.type) -> f.setInt(this, json.optInt(name))
                TypeMatch.isLong(f.type) -> f.setLong(this, json.optLong(name))
                TypeMatch.isDouble(f.type) -> f.setDouble(this, json.optDouble(name))
                TypeMatch.isBoolean(f.type) -> f.setBoolean(this, json.optBoolean(name))
                TypeMatch.isList(f.type) -> {
                    // List 类型,去获取其中的泛型
                    val paramType = f.genericType as ParameterizedType
                    // 得到泛型里的class类型对象
                    val typeClz = paramType.actualTypeArguments[0] as Class<*>
                    f.set(this, getListFromJsonArray(typeClz, json.getJSONArray(name)))
                }
                TypeMatch.isBaseJsonEntity(f.type) -> {
                    // 同样继承了当前类的变量
                    val obj = f.type.newInstance()
                    val method = f.type.getMethod("setFromJson",
                        JSONObject::class.java, Array<String>::class.java)
                    val array = emptyArray<String>()
                    method.invoke(obj, json.optJSONObject(name), array)
                    f.set(this, obj)
                }
                else -> f.set(this, json.opt(name))
            }
        }
    }

    // 获取array数据
    private fun getListFromJsonArray(clz: Class<*>, array: JSONArray) : List<Any> {
        val list: MutableList<Any> = arrayListOf()
        for (i in 0 until array.length()) {
            when {
                TypeMatch.isString(clz) -> list.add(array.optString(i))
                TypeMatch.isInt(clz) -> list.add(array.optInt(i))
                TypeMatch.isLong(clz) -> list.add(array.optLong(i))
                TypeMatch.isDouble(clz) -> list.add(array.optDouble(i))
                TypeMatch.isBoolean(clz) -> list.add(array.optBoolean(i))
                TypeMatch.isBaseJsonEntity(clz) -> {
                    // 同样继承了当前类的变量
                    val obj = clz.newInstance()
                    val method = clz.getMethod("setFromJson",
                        JSONObject::class.java, Array<String>::class.java)
                    val temp = emptyArray<String>()
                    method.invoke(obj, array.optJSONObject(i), temp)
                    list.add(obj)
                }
            }
        }
        return list
    }

    /**
     * 转换为JSON
     * @param excludeNames 排除的变量
     */
    open fun toJson(vararg excludeNames: String) : JSONObject {
        val json = JSONObject()
        val nameList = excludeNames.asList()
        // 获取所有字段,包含父类的
        val fs: MutableList<Field> = mutableListOf()
        var tempClass: Class<in Any>? = this.javaClass
        while (tempClass != null) {
            fs.addAll(tempClass.declaredFields)
            tempClass = tempClass.superclass
        }
        for (f in fs) {
            // 排除不符合的变量
            val annotation = f.getAnnotation(DLField::class.java) ?: continue
            if (nameList.contains(f.name)) continue
            val modifier = Modifier.toString(f.modifiers)
            if (modifier.contains("static") || modifier.contains("final")) continue
            // 提取json中的字段名
            var name = annotation.nameInJson
            if (name.isEmpty()) {
                name = f.name
            }
            // 将private变量改成外部类可读
            f.isAccessible = true
            // 判断变量类型
            when {
                TypeMatch.isList(f.type) -> {
                    // List 类型,去获取其中的泛型
                    val paramType = f.genericType as ParameterizedType
                    // 得到泛型里的class类型对象
                    val typeClz = paramType.actualTypeArguments[0] as Class<*>
                    var value = f.get(this) as List<Any>?
                    if (value == null) value = emptyList()
                    json.put(name, toJsonArray(typeClz, value))
                }
                TypeMatch.isBaseJsonEntity(f.type) -> {
                    // 同样继承了当前类的变量
                    val instance = f.get(this)
                    val method = f.type.getMethod("toJson", Array<String>::class.java)
                    val temp: JSONObject = method.invoke(instance, emptyArray<String>()) as JSONObject
                    json.put(name, temp)
                }
                else -> json.put(name, f.get(this))
            }
        }
        return json
    }

    // list转换为JSONArray
    private fun toJsonArray(clz: Class<*>, value: List<Any>): JSONArray {
        val array = JSONArray()
        for (i in value.indices) {
            if (TypeMatch.isBaseJsonEntity(clz)) {
                // 同样继承了当前类的变量
                val method = clz.getMethod("toJson", Array<String>::class.java)
                val temp: JSONObject = method.invoke(value[i], emptyArray<String>()) as JSONObject
                array.put(temp)
            } else {
                array.put(value[i])
            }
        }
        return array
    }
}

使用方法

你可以直接复制上面的代码进你的工程使用,也可以添加依赖库的形式

添加依赖

1、在根目录的build.gradle里插入

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

2、在app的build.gradle里插入

	dependencies {
	        implementation 'com.github.D10NGYANG:JsonEntityManager:1.1'
	}

定义实体类继承BaseJsonEntity

可以使用插件快速创建,详情查看:
Android KotlinJSON快速生成实体类插件使用
但是生成实体类的并非可以直接使用,需要根据情况修改

测试实体类

data class TestInfo (

    // 使用nameInJson标注在JSON中对应的字段
    @DLField(nameInJson = "int1")
    var intT: Int = 0,

    @DLField
    var stringT: String = "",

    // 这是一个普通Array,用于读取JSON中的JSONArray
    @DLField
    var listT: List<String> = emptyList(),

    // 这是另一个继承了BaseJsonEntity()的变量
    @DLField
    var test2: Test2Info = Test2Info(),

    // 这是继承了BaseJsonEntity()的变量列表
    @DLField
    var testList: List<Test2Info> = emptyList()
) : BaseJsonEntity()

另一个

data class Test2Info (
    @DLField
    var para1: String = "0",
    @DLField
    var para2: Int = 2,
    @DLField
    var para3: Boolean = false
) : BaseJsonEntity()

demo

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // {"int1":3,"listT":["1a","2b","3c"],"stringT":"abcd","test2":{"para1":"efgh","para2":1234,"para3":true},"testList":[{"para1":"L0","para2":0,"para3":false},{"para1":"L1","para2":1,"para3":true}]}
        val str = "{\"int1\":3,\"listT\":[\"1a\",\"2b\",\"3c\"],\"stringT\":\"abcd\",\"test2\":{\"para1\":\"efgh\",\"para2\":1234,\"para3\":true},\"testList\":[{\"para1\":\"L0\",\"para2\":0,\"para3\":false},{\"para1\":\"L1\",\"para2\":1,\"para3\":true}]}"
        val json = JSONObject(str)

        val test = TestInfo()
        test.setFromJson(json)

        Log.e("测试", "json=${test.toJson()}")
    }
}

混淆规则

# 实体转换工具
-keep class com.dlong.jsonentitylib.** {*;}
-dontwarn com.dlong.jsonentitylib.**

GitHub

https://github.com/D10NGYANG/JsonEntityManager

更新2020/2/22

你可能感兴趣的:(Android知识线)