Moshi

Moshi是一个对Kotlin更友爱的Json库,square/moshi: A modern JSON library for Kotlin and Java. (github.com)

依靠

implementation("com.squareup.moshi:moshi:1.8.0")
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.8.0")

运用场景

基于kotlin-reflection反射需要额定增加 com.squareup.moshi:moshi-kotlin:1.13.0 依靠

// generateAdapter = true 表明运用codegen生成这个类的JsonAdapter
@JsonClass(generateAdapter = true)
// @Json 标识json中字段名
data class Person(@Json(name = "_name")val name: String, val age: Int)
fun main() {
  val moshi: Moshi = Moshi.Builder()
    // KotlinJsonAdapterFactory基于kotlin-reflection反射创立自定义类型的JsonAdapter
     .addLast(KotlinJsonAdapterFactory())
     .build()
  val json = """{"_name": "xxx", "age": 20}"""
  val person = moshi.adapter(Person::class.java).fromJson(json)
  println(person)
}
  • KotlinJsonAdapterFactory用于反射生成数据类的JsonAdapter,假如不运用codegen,那么这个配置是必要的;假如有多个factory,一般将KotlinJsonAdapterFactory增加到终究,因为创立Adapter时是次序遍历factory进行创立的,应该把反射创立作为终究的手段
  • @JsonClass(generateAdapter = true)标识此类,让codegen在编译期生成此类的JsonAdapter,codegen需要数据类和它的properties可见性都是internal/public
  • moshi不允许需要序列化的类不是存粹的Java/Kotlin类,比如说Java承继Kotlin或许Kotlin承继Java

存在的问题

一切的字段都有默许值的状况

@JsonClass(generateAdapter = true)
data class DefaultAll(
   val name: String = "me",
   val age: Int = 17
)

这种状况下,gson 和 moshi都能够正常解析 “{}” json字符

部分字段有默许值

@JsonClass(generateAdapter = true)
data class DefaultPart(
   val name: String = "me",
   val gender: String = "male",
   val age: Int
)
​
// 针对以下json gson疏忽name,gender特点的默许值,而moshi能够正常解析
val json = """{"age": 17}"""

发生的原因

Gson反序列化目标时优先获取无参结构函数,因为DefaultPart age特点没有默许值,在生成字节码文件后,该类没有无参结构函数,一切Gson终究调用了Unsafe.newInstance函数,该函数不会调用结构函数,执行目标初始化代码,导致name,gender目标是null。

Moshi 经过adpter的方法匹配类的结构函数,运用函数签名最相近的结构函数结构目标,能够是的默许值不丢失,但在官方的例程中,某些状况下仍然会出现咱们不希望出现的问题。

Moshi的特殊Json场景

1、特点缺失

针对以下类

@JsonClass(generateAdapter = true)
data class DefaultPart(
  val name: String,
  val gender: String = "male",
  val age: Int
)

若json = “”” {“name”:”John”,”age”:18}””” Moshi能够正常解析,但假如Json=””” {“name”:”John”}”””Moshi会抛出Required value age missing at $ 的反常,

2、特点=null

若Json = “””{“name”:”John”,”age”:null} ”“”Moshi会抛出Non-null value age was null at $ 的反常

许多时分后台返回的Json数据并不是完全的统一,会存在以上状况,咱们能够经过对age特点如gender特点一般设置默许值的方法处理,但可不能够更偷闲一点,能够不必写默许值,体系也能给一个默许值出来。

完善Moshi

分析官方库KotlinJsonAdapterFactory类,发现,以上两个逻辑的判别代码在这里

internal class KotlinJsonAdapter<T>(
 val constructor: KFunction<T>,
  // 一切特点的bindingAdpter
 val allBindings: List<Binding<T, Any?>?>,
  // 疏忽反序列化的特点
 val nonIgnoredBindings: List<Binding<T, Any?>>,
  // 反射类得来的特点列表
 val options: JsonReader.Options
) : JsonAdapter<T>() {
​
 override fun fromJson(reader: JsonReader): T {
  val constructorSize = constructor.parameters.size
​
  // Read each value into its slot in the array.
  val values = Array<Any?>(allBindings.size) { ABSENT_VALUE }
  reader.beginObject()
  while (reader.hasNext()) {
    //经过reader获取到Json 特点对应的类特点的索引
   val index = reader.selectName(options)
   if (index == -1) {
    reader.skipName()
    reader.skipValue()
    continue
    }
    //拿到该特点的binding
   val binding = nonIgnoredBindings[index]
        // 拿到特点值的索引
   val propertyIndex = binding.propertyIndex
   if (values[propertyIndex] !== ABSENT_VALUE) {
    throw JsonDataException(
     "Multiple values for '${binding.property.name}' at ${reader.path}"
     )
    }
        // 递归的方法,初始化特点值
   values[propertyIndex] = binding.adapter.fromJson(reader)
​
    // 要害的当地1
    // 判别 初始化的特点值是否为null ,假如是null ,代表这json字符串中的体现为 age:null 
   if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
     // 抛出Non-null value age was null at $ 反常
    throw Util.unexpectedNull(
     binding.property.name,
     binding.jsonName,
     reader
     )
    }
   }
  reader.endObject()
​
  // 要害的当地2
   // 初始化剩余json中没有的特点
  // Confirm all parameters are present, optional, or nullable.
   // 是否调用全特点结构函数标志
  var isFullInitialized = allBindings.size == constructorSize
  for (i in 0 until constructorSize) {
   if (values[i] === ABSENT_VALUE) {
     // 假如等于ABSENT_VALUE,表明该特点没有初始化
    when {
      // 假如该特点是可缺失的,即该特点有默许值,这不需要处理,全特点结构函数标志为false
     constructor.parameters[i].isOptional -> isFullInitialized = false
      // 假如该特点是可空的,这直接赋值为null
     constructor.parameters[i].type.isMarkedNullable -> values[i] = null // Replace absent with null.
      // 剩余的则是特点没有默许值,也不允许为空,如上例,age特点
      // 抛出Required value age missing at $ 反常
     else -> throw Util.missingProperty(
      constructor.parameters[i].name,
      allBindings[i]?.jsonName,
      reader
      )
     }
    }
   }
​
  // Call the constructor using a Map so that absent optionals get defaults.
  val result = if (isFullInitialized) {
   constructor.call(*values)
   } else {
   constructor.callBy(IndexedParameterMap(constructor.parameters, values))
   }
​
  // Set remaining properties.
  for (i in constructorSize until allBindings.size) {
   val binding = allBindings[i]!!
   val value = values[i]
   binding.set(result, value)
   }
​
  return result
  }
​
 override fun toJson(writer: JsonWriter, value: T?) {
  if (value == null) throw NullPointerException("value == null")
​
  writer.beginObject()
  for (binding in allBindings) {
   if (binding == null) continue // Skip constructor parameters that aren't properties.
​
   writer.name(binding.jsonName)
   binding.adapter.toJson(writer, binding.get(value))
   }
  writer.endObject()
  }
​

经过代码的分析,是不是能够在两个要害的逻辑点做以下修改

// 要害的当地1
// 判别 初始化的特点值是否为null ,假如是null ,代表这json字符串中的体现为 age:null 
if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
  // 抛出Non-null value age was null at $ 反常
  //throw Util.unexpectedNull(
  //   binding.property.name,
  //   binding.jsonName,
  //   reader
  //)
  // age:null 重置为ABSENT_VALUE值,交由终究初始化剩余json中没有的特点的时分去初始化
    values[propertyIndex] = ABSENT_VALUE
}
​
// 要害的当地2
// 初始化剩余json中没有的特点
// Confirm all parameters are present, optional, or nullable.
// 是否调用全特点结构函数标志
var isFullInitialized = allBindings.size == constructorSize
for (i in 0 until constructorSize) {
  if (values[i] === ABSENT_VALUE) {
    // 假如等于ABSENT_VALUE,表明该特点没有初始化
    when {
      // 假如该特点是可缺失的,即该特点有默许值,这不需要处理,全特点结构函数标志为false
      constructor.parameters[i].isOptional -> isFullInitialized = false
      // 假如该特点是可空的,这直接赋值为null
      constructor.parameters[i].type.isMarkedNullable -> values[i] = null // Replace absent with null.
      // 剩余的则是特点没有默许值,也不允许为空,如上例,age特点
      // 抛出Required value age missing at $ 反常
      else ->{
        //throw Util.missingProperty(
          //constructor.parameters[i].name,
          //allBindings[i]?.jsonName,
          //reader
           //)
        // 填充默许
        val index = options.strings().indexOf(constructor.parameters[i].name)
        val binding = nonIgnoredBindings[index]
        val propertyIndex = binding.propertyIndex
                // 为该特点初始化默许值
        values[propertyIndex] = fullDefault(binding)
​
       }
     }
   }
}
​
​
​
private fun fullDefault(binding: Binding<T, Any?>): Any? {
    return when (binding.property.returnType.classifier) {
      Int::class -> 0
      String::class -> ""
      Boolean::class -> false
      Byte::class -> 0.toByte()
      Char::class -> Char.MIN_VALUE
      Double::class -> 0.0
      Float::class -> 0f
      Long::class -> 0L
      Short::class -> 0.toShort()
      // 过滤递归类初始化,这种会导致死循环
      constructor.returnType.classifier -> {
        val message =
          "Unsolvable as for: ${binding.property.returnType.classifier}(value:${binding.property.returnType.classifier})"
        throw JsonDataException(message)
       }
      is Any -> {
        // 假如是调集就初始化[],否则就是{}目标
        if (Collection::class.java.isAssignableFrom(binding.property.returnType.javaType.rawType)) {
          binding.adapter.fromJson("[]")
         } else {
          binding.adapter.fromJson("{}")
         }
       }
      else -> {}
     }
   }

终究作用

“””{“name”:”John”,”age”:null} ”“” age会被初始化成0,

“””{“name”:”John”} ”“” age仍然会是0,即便咱们在类中没有定义age的默许值

甚至是目标

@JsonClass(generateAdapter = true)
data class DefaultPart(
  val name: String,
  val gender: String = "male",
  val age: Int,
  val action:Action
)
class Action(val ac:String)

终究Action也会发生一个Action(ac:””)的值

data class RestResponse<T>(
    val code: Int,
    val msg: String="",
    val data: T?
) {
    fun isSuccess() = code == 1
    fun checkData() = data != null
    fun successRestData() = isSuccess() && checkData()
    fun requsetData() = data!!
}
class TestD(val a:Int,val b:String,val c:Boolean,val d:List<Test> ) {
}
class Test(val a:Int,val b:String,val c:Boolean=true)
val s = """
                {
                    "code":200,
                    "msg":"ok",
                    "data":[{"a":0,"c":false,"d":[{"b":null}]}]}
            """.trimIndent()
val a :RestResponse<List<TestD>>? = s.fromJson()

终究a为 {“code”:200,”msg”:”ok”,”data”:[{“a”:0,”b”:””,”c”:false,”d”:[{“a”:0,”b”:””,”c”:true}]}]}