我正在尝试反序列化一个json响应,它包含的对象具有可以根据父类中的属性更改类型的子对象。我看过一些例子,说明如何使用类型适配器工厂来反序列化一个定义了自己的属性类型的子对象,但是不知道如何在定义类型位于父对象中的情况下进行反序列化。这可能吗?
JSON示例
{
"items": [
{
"someProperty": "here",
"anotherProperty": "there",
"childProperty": {
"foo": "This property will be here if itemType is 'foo'"
"abc": "def"
},
"itemType": "foo",
},
{
"someProperty": "here",
"anotherProperty": "there",
"childProperty": {
"bar": "This property will be here if itemType is 'bar'"
"ghi": "jkl"
},
"itemType": "bar",
}
],
"limit": 25,
"nextCursor": null
}
在上面的示例中,childPropertyThatChanges
应根据itemType
的值反序列化为不同的类型。
给定以下序列化的类:
data class FooBarWrapper(
val items: List<ParentItem>,
val limit: Int,
val nextCursor: String?
) : Serializable
data class ParentItem(
val someProperty: String,
val anotherProperty: String,
val childProperty: ChildProperty
)
open class ChildProperty
data class ChildPropertyFoo(
val foo: String,
val abc: String
) : ChildProperty()
data class ChildPropertyBar(
val bar: String,
val ghi: String
) : ChildProperty()
和类型适配器为:
val exampleTypeAdapter = RuntimeTypeAdapterFactory
.of(ChildProperty::class.java, "itemType")
.registerSubtype(ChildPropertyFoo::class.java, "foo")
.registerSubtype(ChildPropertyBar::class.java, "bar")
val exampleGson = GsonBuilder()
.registerTypeAdapterFactory(exampleTypeAdapter)
.create()
val deserialized = exampleGson.fromJson(exampleJson, FooBarWrapper::class.java)
在上述范例中,childProperty
永远不会还原序列化,因为itemType
存在于父对象中,所以无法推断型别,所以会维持null。
但是,如果我将json模式更改为下面的模式,其中itemType位于子对象内部,则一切都可以很好地反序列化。
{
"items": [{
"someProperty": "here",
"anotherProperty": "there",
"childPropertyThatChanges": {
"foo": "here when itemType is foo",
"abc": "def",
"itemType": "foo"
}
},
{
"someProperty": "here",
"anotherProperty": "there",
"childPropertyThatChanges": {
"bar": "here when itemType is bar",
"ghi": "jkl",
"itemType": "bar"
}
}
],
"limit": 25,
"nextCursor": null
}
我不能改变我正在接收的json,所以我试图弄清楚如何创建类型适配器,以便它与父对象和子对象中定义的类型一起工作。
2条答案
按热度按时间ki1q1bka1#
使用Gson,您可以通过实现一个自定义的
TypeAdapterFactory
来解决这个问题,该TypeAdapterFactory
执行以下操作:1.验证请求的类型是否为
ParentItem
1.创建一个从
itemType
字符串到对应的TypeAdapter
的Map,从Gson
示例中获得(以下称为“itemType
Map”)1.从
Gson
示例中获取JsonObject
的适配器(在下面称为“JsonObject适配器”)1.从
Gson
示例中获取ParentItem
的 * 委托适配器 *(以下称为“ParentItem适配器”)(需要 * 委托 * 适配器,因为否则Gson将简单地使用当前ParentItem
工厂,导致无限递归)1.创建并返回执行以下操作的适配器:
1.使用JsonObject适配器从读取器读取
1.从已解析的JsonObject中删除
childProperty
值,并将其存储在变量childPropertyValue
中1.移除
itemType
值,并从itemType
Map中取得Map的TypeAdapter(以下称为“子配接卡”)1.在已解析的JsonObject上使用ParentItem适配器(没有
childPropertyValue
; Gson不会抱怨丢失的财产)1.在
childPropertyValue
上使用子适配器,并将其结果存储在先前读取的ParentItem对象的childProperty
中(这需要将ParentItem.childProperty
设置为var
)1.返回ParentItem对象
然后,您只需要将该
TypeAdapterFactory
注册到GsonBuilder
(以及可选的ChildPropertyFoo
或ChildPropertyBar
的任何自定义适配器)。下面是
TypeAdapterFactory
的示例实现:请注意,其他JSON框架提供了这种现成的功能,例如Jackson有
JsonTypeInfo.As.EXTERNAL_PROPERTY
。rn0zuynd2#
一种方法是为
ParentItem
类创建一个类型适配器,并在JsonDeserializer
子类中基于itemType
属性的值使用正确的类反序列化子对象(ChildPropertyFoo
或ChildPropertyBar
)。然后,您可以简单地将反序列化的对象赋给ChildProperty
属性。但是,这将需要在ParentItem
中将childProperty
更改为var,因为它需要重新分配。代码可能如下所示:
当然,可以通过将
itemType
、childProperty
等细节注入ItemDeserializer
示例来概括整个过程,但我更希望展示基本方法。无论如何,要获得一个用于快速测试的自包含示例,调用仍然缺失,可能如下所示:
然后调试控制台将输出以下内容,您可以看到反序列化代码给出了所需的结果: