paquete mínimo de moshi


prefacio

Escribí un artículo antes para presentar el uso básico y el combate real de moshi. Si está interesado, primero puede echar un vistazo al uso básico y el combate real de moshi, una biblioteca JSON moderna compatible con kotlin.

En ese artículo, finalmente creamos un paquete para moshi, el código es más o menos el siguiente:

import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import java.lang.reflect.ParameterizedType

/**
 * @description: 基于moshi的json转换封装
 * @author : yuzhiqiang ([email protected])
 * @date   : 2022/3/13
 * @time   : 6:29 下午
 */

object MoshiUtil {
    
    

    val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()

    fun <T> toJson(adapter: JsonAdapter<T>, src: T, indent: String = ""): String {
    
    
        try {
    
    
            return adapter.indent(indent).toJson(src)
        } catch (e: Exception) {
    
    
            e.printStackTrace()
        }
        return ""

    }

    /**
     * T 类型对象序列化为 json
     * @param src T
     * @param indent String
     * @return String
     */
    inline fun <reified T> toJson(src: T, indent: String = ""): String {
    
    
        val adapter = moshi.adapter(T::class.java)
        return this.toJson(adapter = adapter, src = src, indent = indent)
    }


    /**
     * 将 T 序列化为 json,指定 parameterizedType,适合复杂类型
     * @param src T
     * @param parameterizedType ParameterizedType
     * @param indent String
     * @return String
     */
    inline fun <reified T> toJson(src: T, parameterizedType: ParameterizedType, indent: String = ""): String {
    
    
        val adapter = moshi.adapter<T>(parameterizedType)
        return this.toJson(adapter = adapter, src = src, indent = indent)
    }

    inline fun <reified T> fromJson(adapter: JsonAdapter<T>, jsonStr: String): T? {
    
    
        try {
    
    
            return adapter.fromJson(jsonStr)
        } catch (e: Exception) {
    
    
            e.printStackTrace()
        }
        return null
    }

    /**
     * json 反序列化为 T
     * @param jsonStr String
     * @return T?
     */
    inline fun <reified T> fromJson(jsonStr: String): T? {
    
    
        val adapter = moshi.adapter(T::class.java)
        return this.fromJson(adapter, jsonStr)
    }

    /**
     * json 反序列化为 MutableList<T>
     * @param jsonStr String
     * @return MutableList<T>?
     */
    inline fun <reified T> fromJsonToList(jsonStr: String): MutableList<T>? {
    
    
        val parameterizedType = Types.newParameterizedType(MutableList::class.java, T::class.java)
        return fromJson<MutableList<T>>(jsonStr, parameterizedType)
    }

    /**
     * json 反序列化为 T, 指定 parameterizedType,复杂数据用
     * @param jsonStr String
     * @param parameterizedType ParameterizedType
     * @return T?
     */
    inline fun <reified T> fromJson(jsonStr: String, parameterizedType: ParameterizedType): T? {
    
    
        val adapter = moshi.adapter<T>(parameterizedType)
        return this.fromJson(adapter = adapter, jsonStr = jsonStr)
    }

}

Aunque puede satisfacer nuestras necesidades de uso diario, obviamente no es lo suficientemente conciso para usarlo. La
razón es que si el tipo genérico pasado es un tipo genérico anidado y relativamente complejo, no hay mejor manera de obtener el tipo genérico. type, entonces en ese momento, elegí un formulario que separa el objeto de la Lista para hacer un paquete, y agregué otro paquete para la estructura fija de BaseResp. Aunque se puede lograr el efecto, los usuarios deben elegir diferentes métodos para diferentes escenarios, y hay algunos costos de memoria en el desarrollo real.

En el artículo anterior, también se mencionó que Jackson proporciona un procesamiento de módulo kotlin separado para kotlin. Al usar jackson, descubrí que la API de jackson es muy simple. ¿Cómo lo maneja jackson? ¿Podemos aprender de él?

Uso básico de Jackson

En primer lugar, echemos un vistazo al uso de jackson.
El uso básico de jackson también es muy simple
para agregar dependencias.

    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3")

Serialización y deserialización

 val userList = mutableListOf<User>()
    (1..10).forEach {
    
    
        val user = User("name${
      
      it}", it, arrayListOf(Hobby("类型", "爱好${
      
      it}")))
        userList.add(user)
    }

    val baseResp = BaseResp(200, "ok", userList)
    /*序列化*/
    val baseRespJsonStr = jacksonObjectMapper().writeValueAsString(baseResp)
    println("baseRespJsonStr = ${
      
      baseRespJsonStr}")
    /*反序列化*/
    val baseRespList = jacksonObjectMapper().readValue<BaseResp<List<User>>>(baseRespJsonStr)
    println("baseRespList = ${
      
      baseRespList}")

Sí, leyó bien, es así de simple, echemos un vistazo a los resultados de ejecución primero

inserte la descripción de la imagen aquí

En otras palabras, jackson-kotlin-module nos ha proporcionado una API buena y concisa, y apenas necesitamos empaquetarla cuando la usamos.

El ingenioso manejo de tipos genéricos de Jackson

Entonces, ¿cómo lo hace Jackson? Echemos un vistazo al código fuente, principalmente el código fuente del método de deserialización.

inserte la descripción de la imagen aquí
Se puede ver que readValue es un método de extensión de ObjectMapper, y finalmente se llama al método readValue.
inserte la descripción de la imagen aquí

El punto clave al que debemos prestar atención es cómo obtiene Jackson la información del tipo genérico.
inserte la descripción de la imagen aquí
El punto clave es este jacksonTypeRef(), echemos un vistazo a lo que es esto.
Después de hacer clic para encontrar que es un objeto anónimo de TypeReference<T>tipo
inserte la descripción de la imagen aquí
, echemos un vistazoTypeReference<T>

inserte la descripción de la imagen aquí
Se encuentra que TypeReference es en realidad una clase abstracta genérica, y el tipo parametrizado se obtiene en el método de construcción proporcionado.
El principio es concretar el tipo genérico a través de subclases.De hecho, si piensa detenidamente en el TypeToken en Gson que usamos antes, es un enfoque similar.

Aprende de jackson para optimizar el empaque de moshi

Entonces será fácil de manejar después de conocer el principio.Podemos copiar el paquete anterior y modificarlo.

En primer lugar, también preparamos una clase abstracta para envolver genéricos.

 abstract class MoshiTypeReference<T> // 自定义的类,用来包装泛型

Entonces es proporcionar un método para obtener el tipo genérico específico a través de la subclase

    inline fun <reified T> getGenericType(): Type {
    
    
        val type =
            object :
                MoshiTypeReference<T>() {
    
    }::class.java
                .genericSuperclass
                .let {
    
     it as ParameterizedType }
                .actualTypeArguments
                .first()
        return type

Es fácil obtener el tipo, eliminar todos los métodos de encapsulación redundantes anteriores, solo necesitamos mantener dos métodos, el
código completo de toJson y fromJson es el siguiente, copie y use directamente

import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.xeon.json.kotlin.data.polym.moshi.Employee
import com.xeon.json.kotlin.data.polym.moshi.Person
import com.xeon.json.kotlin.data.polym.moshi.Student
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
 * @description: 基于moshi的json转换封装
 * @author : yuzhiqiang ([email protected])
 * @date   : 2022/3/13
 * @time   : 6:29 下午
 */

object MoshiUtil {
    
    

    abstract class MoshiTypeReference<T> // 自定义的类,用来包装泛型

    val moshi = Moshi.Builder()
        .addLast(KotlinJsonAdapterFactory()).build()

    inline fun <reified T> toJson(src: T, indent: String = ""): String {
    
    
        
        try {
    
    

            val jsonAdapter = moshi.adapter<T>(getGenericType<T>())
            return jsonAdapter.indent(indent).toJson(src)
        } catch (e: Exception) {
    
    
            e.printStackTrace()
        }
        return ""

    }

    inline fun <reified T> fromJson(jsonStr: String): T? {
    
    
        try {
    
    
            val jsonAdapter = moshi.adapter<T>(getGenericType<T>())
            return jsonAdapter.fromJson(jsonStr)
        } catch (e: Exception) {
    
    
            e.printStackTrace()
        }
        return null
    }


    inline fun <reified T> getGenericType(): Type {
    
    
        val type =
            object :
                MoshiTypeReference<T>() {
    
    }::class.java
                .genericSuperclass
                .let {
    
     it as ParameterizedType }
                .actualTypeArguments
                .first()
        return type

    }


}

Bueno, esto no parece mucho más simple, solo dos métodos simples, toJson y fromJson.

usar

Una vez completada la transformación, usemos una ola.

    /*对象反序列化泛型测试*/
    val user = User("喻志强", 19)
    val userStr = MoshiUtil.toJson(user)
    val userObj = MoshiUtil.fromJson<User>(userStr)
    println("userObj = ${
      
      userObj!!.name}")

    /*复杂对象反序列化时的泛型测试*/
    val baseRespJsonStr = """
        {"code":200,"msg":"ok","data":[{"name":"name1","age":1,"hobby":[{"type":"类型","name":"爱好1"}]},{"name":"name2","age":2,"hobby":[{"type":"类型","name":"爱好2"}]},{"name":"name3","age":3,"hobby":[{"type":"类型","name":"爱好3"}]},{"name":"name4","age":4,"hobby":[{"type":"类型","name":"爱好4"}]},{"name":"name5","age":5,"hobby":[{"type":"类型","name":"爱好5"}]},{"name":"name6","age":6,"hobby":[{"type":"类型","name":"爱好6"}]},{"name":"name7","age":7,"hobby":[{"type":"类型","name":"爱好7"}]},{"name":"name8","age":8,"hobby":[{"type":"类型","name":"爱好8"}]},{"name":"name9","age":9,"hobby":[{"type":"类型","name":"爱好9"}]},{"name":"name10","age":10,"hobby":[{"type":"类型","name":"爱好10"}]}]}
    """.trimIndent()
    val baseResp = MoshiUtil.fromJson<BaseResp<List<User>>>(baseRespJsonStr)
    println("baseResp = ${
      
      baseResp}")
    if (baseResp == null) {
    
    
        println("解析异常")
    }

    println(baseResp!!.data.get(0).name)

    val baseRespToJson = MoshiUtil.toJson(baseResp)
    println("baseRespToJson = ${
      
      baseRespToJson}")

    /*直接是一个list测试*/
    val userListJsonStr = MoshiUtil.toJson(baseResp.data)
    println("userListJsonStr = ${
      
      userListJsonStr}")
    val userList = MoshiUtil.fromJson<List<User>>(userListJsonStr)
    println("userList = ${
      
      userList}")

    println(userList!!.get(0).hobby.get(0).name)

resultado de la operación:
inserte la descripción de la imagen aquí

Como puedes ver, funciona bien.
En comparación con el paquete al principio del artículo, el modificado es mucho más cómodo de usar, solo que toJson y fromJson están listos.

De hecho, no entiendo por qué moshi no lo encapsula directamente como jackson, sino que nos brinda una API que es más fácil de usar y permite que los desarrolladores la usen para especificar tipos Types.newParameterizedType. tipos anidados.


Resumir

Bueno, este blog es en realidad el final del blog anterior sobre el combate real de moshi. De hecho, no se sentía bien después de que se empaquetó antes, y no resaltó las características de kotlin en absoluto. No lo hice. Pienso en una buena manera, así que lo usé así por el momento.

Otro sentimiento es que si no está satisfecho con el código que escribe, debe encontrar una manera de optimizarlo, y debe haber una manera de optimizarlo. ¡Póngase en contacto con otros marcos y puede que encuentre una solución!
¡También espero que este blog pueda ser útil para los grandes!


Si crees que este artículo es útil para ti, por favor dale me gusta. Puede ayudar a más desarrolladores. Si hay algún error en el artículo, corrígeme. Para volver a imprimir, indica que estás reenviando desde el blog de Yu Zhiqiang, ¡ gracias !

Supongo que te gusta

Origin blog.csdn.net/yuzhiqiang_1993/article/details/125132064
Recomendado
Clasificación