Abstract classes can't be instantiated! Register an InstanceCreator or a TypeAdapter for this type.什么时候会出现如此异常。下面举个栗子:
import com.google.gson.Gson import com.google.gson.reflect.TypeToken // 堆代码 duidaima.com sealed class Gender object Male: Gender() object Female: Gender() data class Student( val id: Int, val name: String, val gender: Gender ) fun main() { val list1 = listOf( Student(1001, "Jimy", Male), Student(1002, "Lucy", Female), Student(1003, "HanMeimei", Female), Student(1004, "LiLei", Male) ) println("list1: $list1") val jsonString = Gson().toJson(list1) println("jsonString: $jsonString") try { val typeToken = object : TypeToken<List<Student>>() {}.type val list2: List<Student> = Gson().fromJson(jsonString, typeToken) println("list2: $list2") } catch (ex: Exception) { println("catch: ${ex.message}") } }上面的代码,执行结果如下:
list1: [Student(id=1001, name=Jimy, gender=serialize.gson.Male@79fc0f2f), Student(id=1002, name=Lucy, gender=serialize.gson.Female@50040f0c), Student(id=1003, name=HanMeimei, gender=serialize.gson.Female@50040f0c), Student(id=1004, name=LiLei, gender=serialize.gson.Male@79fc0f2f)] jsonString: [{"id":1001,"name":"Jimy","gender":{}},{"id":1002,"name":"Lucy","gender":{}},{"id":1003,"name":"HanMeimei","gender":{}},{"id":1004,"name":"LiLei","gender":{}}] catch: Abstract classes can't be instantiated! Register an InstanceCreator or a TypeAdapter for this type. Class name: serialize.gson.Gender从这个输出结果,我们可以看到两个问题:
2. jsonString 反序列化过程中发生了异常。
Abstract classes can't be instantiated! Register an InstanceCreator or a TypeAdapter for this type.抽象类无法实例化!为此类型注册 InstanceCreator 或 TypeAdapter。
class GenderTypeAdapter: TypeAdapter<Gender>() { override fun write(out: JsonWriter?, value: Gender?) { out?.value(value?.javaClass?.name) } override fun read(`in`: JsonReader?): Gender { return when(val className = `in`?.nextString()) { Male::class.java.name -> Male Female::class.java.name -> Female else -> throw IllegalArgumentException("Unknown class name: $className") } } }然后为 Gson 对象注册该 typeAdapter
fun main() { val list1 = listOf( Student(1001, "Jimy", Male), Student(1002, "Lucy", Female), Student(1003, "HanMeimei", Female), Student(1004, "LiLei", Male) ) println("list1: $list1") // I'm here val jsonString = GsonBuilder().registerTypeAdapter(Gender::class.java, GenderTypeAdapter()).create().toJson(list1) println("jsonString: $jsonString") try { val typeToken = object : TypeToken<List<Student>>() {}.type // I'm here val list2: List<Student> = GsonBuilder().registerTypeAdapter(Gender::class.java, GenderTypeAdapter()).create().fromJson(jsonString, typeToken) println("list2: $list2") } catch (ex: Exception) { println("catch: ${ex.message}") } }此时执行结果如下:
list1: [Student(id=1001, name=Jimy, gender=serialize.gson.Male@79fc0f2f), Student(id=1002, name=Lucy, gender=serialize.gson.Female@50040f0c), Student(id=1003, name=HanMeimei, gender=serialize.gson.Female@50040f0c), Student(id=1004, name=LiLei, gender=serialize.gson.Male@79fc0f2f)] jsonString: [{"id":1001,"name":"Jimy","gender":"serialize.gson.Male"},{"id":1002,"name":"Lucy","gender":"serialize.gson.Female"},{"id":1003,"name":"HanMeimei","gender":"serialize.gson.Female"},{"id":1004,"name":"LiLei","gender":"serialize.gson.Male"}] list2: [Student(id=1001, name=Jimy, gender=serialize.gson.Male@79fc0f2f), Student(id=1002, name=Lucy, gender=serialize.gson.Female@50040f0c), Student(id=1003, name=HanMeimei, gender=serialize.gson.Female@50040f0c), Student(id=1004, name=LiLei, gender=serialize.gson.Male@79fc0f2f)]Ok, 没有问题。
参数默认值没有效果。Kotlin 属性可以赋予默认值。但是当使用 Gson 时,将会失去效果。
sealed class Gender object Male: Gender() object Female: Gender() data class Student( val id: Int, val name: String = "unknown", val gender: Gender ) fun main() { val json = """ { "id": 1005 } """.trimIndent() try { val stu = Gson().fromJson(json, Student::class.java) println("stu: $stu") } catch (ex: Exception) { println("catch: ${ex.message}") } }这里我们在定义 Student 类是,给 name 属性指定了一个默认值 unknown, 在进行反序列化时,没有指定 name 和 gender, 看看执行结果:
stu: Student(id=1005, name=null, gender=null)结果也表明,name 的默认值没有成功,并且 name 和 gender 都赋值为 null 了。
plugins { id("org.jetbrains.kotlin.plugin.serialization") version("1.4.30") } dependencies 引入: dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") }通过添加 @Serializable 注解,给类进行序列化
package serialize.ktxSerialization import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable sealed class Gender @Serializable object Male: Gender() @Serializable object Female: Gender() @Serializable data class Student( val id: Int, val name: String = "unknown", val gender: Gender )注意:所涉及到的抽象类极其子类都需要加上该注解。
fun main() { val json = """ { "id": 1005 } """.trimIndent() try { val stu = Json.decodeFromString<Student>(json) println("stu: $stu") } catch (ex: Exception) { println("catch: ${ex.message}") } }反序列化的关键方法:
Json.decodeFromString()执行报错了:
catch: Field 'gender' is required for type with serial name 'serialize.ktxSerialization.Student', but it was missing at path: $错误信息指出: gender 属性是必须的。那我们应该如何该如何添加 gender 属性呢?
fun main() { val student = Student(1006, "James", Male) val jsonString = Json.encodeToString(student) println("jsonString: $jsonString") }执行结果如下:
jsonString: {"id":1006,"name":"James","gender":{"type":"serialize.ktxSerialization.Male"}}我们看到,Student 对象序列化之后, gender 对应的 value 是
{"type":"serialize.ktxSerialization.Male"}这里是完整的包名类名。
fun main() { val json = """ { "id": 1005, "gender": {"type": "serialize.ktxSerialization.Female"} } """.trimIndent() try { val stu = Json.decodeFromString<Student>(json) println("stu: $stu") } catch (ex: Exception) { println("catch: ${ex.message}") } }执行结果:
stu: Student(id=1005, name=unknown, gender=serialize.ktxSerialization.Female@36d64342)可以看到,反序列化成功,生成的对象,name 属性赋了默认值。
package serialize.ktxSerialization import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable sealed class Gender @Serializable object Male: Gender() @Serializable object Female: Gender() @Serializable data class Student( val id: Int, val name: String?, // 注意这里 val gender: Gender ) fun main() { val json = """ { "id": 1005, "gender": {"type": "serialize.ktxSerialization.Female"} } """.trimIndent() try { val stu = Json.decodeFromString<Student>(json) println("stu: $stu") } catch (ex: Exception) { println("catch: ${ex.message}") } }我把 name 设置为可空类型,但是没有默认值。这时反序列化是会失败的:
catch: Field 'name' is required for type with serial name 'serialize.ktxSerialization.Student', but it was missing at path: $给 name 属性赋值为 null, 则执行成功
fun main() { val json = """ { "id": 1005, "name", null, "gender": {"type": "serialize.ktxSerialization.Female"} } """.trimIndent() try { val stu = Json.decodeFromString<Student>(json) println("stu: $stu") } catch (ex: Exception) { println("catch: ${ex.message}") } }结果:
stu: Student(id=1005, name=null, gender=serialize.ktxSerialization.Female@340f438e)3.2 用 kotlinx.serialization 解决本文开头的问题
fun main() { val list1 = listOf( Student(1001, "Jimy", Male), Student(1002, "Lucy", Female), Student(1003, "HanMeimei", Female), Student(1004, "LiLei", Male) ) println("list1: $list1") val jsonString = Json.encodeToString(list1) println("jsonString: $jsonString") try { val list2 = Json.decodeFromString<List<Student>>(jsonString) println("list2: $list2") } catch (ex: Exception) { println("catch: ${ex.message}") } }执行结果:
list1: [Student(id=1001, name=Jimy, gender=serialize.ktxSerialization.Male@531d72ca), Student(id=1002, name=Lucy, gender=serialize.ktxSerialization.Female@22d8cfe0), Student(id=1003, name=HanMeimei, gender=serialize.ktxSerialization.Female@22d8cfe0), Student(id=1004, name=LiLei, gender=serialize.ktxSerialization.Male@531d72ca)] jsonString: [{"id":1001,"name":"Jimy","gender":{"type":"serialize.ktxSerialization.Male"}},{"id":1002,"name":"Lucy","gender":{"type":"serialize.ktxSerialization.Female"}},{"id":1003,"name":"HanMeimei","gender":{"type":"serialize.ktxSerialization.Female"}},{"id":1004,"name":"LiLei","gender":{"type":"serialize.ktxSerialization.Male"}}] list2: [Student(id=1001, name=Jimy, gender=serialize.ktxSerialization.Male@531d72ca), Student(id=1002, name=Lucy, gender=serialize.ktxSerialization.Female@22d8cfe0), Student(id=1003, name=HanMeimei, gender=serialize.ktxSerialization.Female@22d8cfe0), Student(id=1004, name=LiLei, gender=serialize.ktxSerialization.Male@531d72ca)]这里很好理解:
如果使用 Kotlin 开发,优先使用高效的序列化框架:kotlinx.serialization。