zde*_*ine 7 gson deserialization kotlin
我不确定这是限制、错误还是 GSON 使用不当。我需要有一个 Kotlin 对象的层次结构(具有各种子类型的父对象),我需要使用 GSON 反序列化它们。反序列化的对象具有正确的子类型,但其字段 enumField 实际上是 null。
首先我认为这是因为该字段被传递给“超级”构造函数,但后来我发现“超级”适用于字符串,只是枚举被破坏了。
看这个例子:
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory
open class Parent(val stringField: String,
val enumField: EnumField) {
enum class EnumField {
SUBTYPE1,
SUBTYPE2,
SUBTYPE3
}
}
class Subtype1() : Parent("s1", EnumField.SUBTYPE1)
class Subtype2(stringField: String) : Parent(stringField, EnumField.SUBTYPE2)
class Subtype3(stringField: String, type: EnumField) : Parent(stringField, type)
val subtypeRAF = RuntimeTypeAdapterFactory.of(Parent::class.java, "enumField")
.registerSubtype(Subtype1::class.java, Parent.EnumField.SUBTYPE1.name)
.registerSubtype(Subtype2::class.java, Parent.EnumField.SUBTYPE2.name)
.registerSubtype(Subtype3::class.java, Parent.EnumField.SUBTYPE3.name)
fun main() {
val gson = GsonBuilder()
.registerTypeAdapterFactory(subtypeRAF)
.create()
serializeAndDeserialize(gson, Subtype1()) // this works (but not suitable)
serializeAndDeserialize(gson, Subtype2("s2")) // broken
serializeAndDeserialize(gson, Subtype3("s3", Parent.EnumField.SUBTYPE3)) // broken
}
private fun serializeAndDeserialize(gson: Gson, obj: Parent) {
println("-----------------------------------------")
val json = gson.toJson(obj)
println(json)
val obj = gson.fromJson(json, Parent::class.java)
println("stringField=${obj.stringField}, enumField=${obj.enumField}")
}
Run Code Online (Sandbox Code Playgroud)
任何想法如何实现反序列化enumField?
(deps: com.google.code.gson:gson:2.8.5 , org.danilopianini:gson-extras:0.2.1 )
PS:请注意,我必须使用,RuntimeAdapterFactory因为我有具有不同字段集的子类型(我在示例中没有这样做,所以更容易理解)。
Gson 需要不带参数的构造函数才能正常工作(请参阅下面深入了解 Gson 代码)。Gson 构造原始对象,然后使用反射用值填充字段。
因此,如果您只是向缺少它们的类添加一些无参数的虚拟构造函数,如下所示:
class Subtype1() : Parent("s1", EnumField.SUBTYPE1)
class Subtype2(stringField: String) : Parent(stringField, EnumField.SUBTYPE2) {
constructor() : this("")
}
class Subtype3(stringField: String, type: EnumField) : Parent(stringField, type) {
constructor() : this("", EnumField.SUBTYPE3)
}
Run Code Online (Sandbox Code Playgroud)
你将得到预期的输出:
-----------------------------------------
{"stringField":"s1","enumField":"SUBTYPE1"}
stringField=s1, enumField=SUBTYPE1
-----------------------------------------
{"stringField":"s2","enumField":"SUBTYPE2"}
stringField=s2, enumField=SUBTYPE2
-----------------------------------------
{"stringField":"s3","enumField":"SUBTYPE3"}
stringField=s3, enumField=SUBTYPE3
Run Code Online (Sandbox Code Playgroud)
如果你想研究 Gson 的内部结构,一个技巧是添加一个init { }块,Subtype1因为它可以工作,然后在那里设置一个断点。命中后,您可以向上移动调用堆栈、单步执行代码、设置更多断点等,以揭示 Gson 如何构造对象的详细信息。
通过使用这种方法,您可以找到 Gson 内部类com.google.gson.internal.ConstructorConstructor及其方法,其newDefaultConstructor(Class<? super T>)代码如下(为了简洁起见,我进行了简化):
final Constructor<? super T> constructor = rawType.getDeclaredConstructor(); // rawType is e.g. 'class Subtype3'
Object[] args = null;
return (T) constructor.newInstance(args);
Run Code Online (Sandbox Code Playgroud)
即它尝试通过不带参数的构造函数构造一个对象。对于Subtype2and的情况Subtype3,代码将导致捕获异常:
} catch (NoSuchMethodException e) { // java.lang.NoSuchMethodException: Subtype3.<init>()
return null; // set breakpoint here to see
}
Run Code Online (Sandbox Code Playgroud)
即您的原始代码失败,因为 Gson 找不到没有参数的构造函数Subtype2和Subtype3。
newUnsafeAllocator(Type, final Class<? super T>)在简单的情况下,缺少无参数构造函数的问题可以使用-method in解决ConstructorConstructor,但RuntimeTypeAdapterFactory不能正常工作。
| 归档时间: |
|
| 查看次数: |
765 次 |
| 最近记录: |