我正在尝试反序列化包含对象的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驻留在父对象中。
但是,如果我将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,因此我正在尝试如何创建类型适配器,以便它与父对象和子对象中定义的类型一起工作。
发布于 2022-02-28 21:14:15
使用Gson,您可以通过实现一个自定义TypeAdapterFactory来解决这个问题,它执行以下操作:
验证请求的类型是ParentItem
itemType字符串到相应TypeAdapter的映射,从Gson实例(在下面称为"itemType映射“)获得
Gson实例获取JsonObject适配器(在下面的名称为"JsonObject
ParentItem工厂,导致无限recursion)
childProperty值,并将其存储在变量childPropertyValueitemType值,并从itemType映射中获得相应的TypeAdapter (在下面的名称为“在解析的JsonObject上的子adapter")childPropertyValue);Gson不会抱怨缺少的property)childPropertyValue上的子适配器,并将其结果存储在以前读取的ParentItem对象的childProperty中(这需要使ParentItem.childProperty成为ParentItem object)。
然后您只需要register that TypeAdapterFactory with a GsonBuilder (也可以选择ChildPropertyFoo或ChildPropertyBar的任何自定义适配器)。
下面是TypeAdapterFactory的一个示例实现
object ParentItemTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
// Only support ParentItem and subtypes
if (!ParentItem::class.java.isAssignableFrom(type.rawType)) {
return null
}
// Safe cast due to check at beginning of function
@Suppress("UNCHECKED_CAST")
val delegateAdapter = gson.getDelegateAdapter(this, type) as TypeAdapter<ParentItem>
val jsonObjectAdapter = gson.getAdapter(JsonObject::class.java)
val itemTypeMap = mapOf(
"foo" to gson.getAdapter(ChildPropertyFoo::class.java),
"bar" to gson.getAdapter(ChildPropertyBar::class.java),
)
// Safe cast due to check at beginning of function
@Suppress("UNCHECKED_CAST")
return object : TypeAdapter<ParentItem>() {
override fun read(reader: JsonReader): ParentItem? {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
return null
}
val parentItemValue = jsonObjectAdapter.read(reader)
val itemType = parentItemValue.remove("itemType").asString
val childAdapter = itemTypeMap[itemType]
?: throw JsonParseException("Invalid item type: $itemType")
val childPropertyValue = parentItemValue.remove("childProperty")
val itemObject = delegateAdapter.fromJsonTree(parentItemValue)
val childObject = childAdapter.fromJsonTree(childPropertyValue)
itemObject.childProperty = childObject
return itemObject
}
override fun write(writer: JsonWriter, value: ParentItem?) {
throw UnsupportedOperationException()
}
} as TypeAdapter<T>
}
}请注意,其他JSON框架提供了这种现成的功能,例如Jackson有JsonTypeInfo.As.EXTERNAL_PROPERTY。
发布于 2022-02-28 21:33:31
一种方法是根据ParentItem属性的值为JsonDeserializer类和JsonDeserializer子类创建一个类型适配器,用正确的类(ChildPropertyFoo或ChildPropertyBar)反序列化子对象。然后,您可以简单地将反序列化对象分配给ChildProperty属性。但是,这需要将childProperty更改为ParentItem中的var,因为它需要重新分配。或者,我们可以构建一个完整的ParentItem。
代码可能如下所示:
import com.google.gson.*
import java.lang.reflect.Type
internal class ItemDeserializer : JsonDeserializer<ParentItem> {
override fun deserialize(
json: JsonElement,
t: Type,
jsonDeserializationContext: JsonDeserializationContext
)
: ParentItem? {
val type = (json as JsonObject)["itemType"].asString
val gson = Gson()
val childJson = json["childProperty"]
val childClass = if (type == "foo") ChildPropertyFoo::class.java else ChildPropertyBar::class.java
val childObject = gson.fromJson<ChildProperty>(childJson, childClass)
val parent = gson.fromJson(json, ParentItem::class.java) as ParentItem
parent.childProperty = childObject
return parent
}
}当然,通过将诸如itemType、childProperty等细节注入到ItemDeserializer实例中,可以对整个过程进行概括,但我更希望展示基本的方法。
无论如何,要获得一个快速测试的自包含示例,仍然缺少调用,它可能如下所示:
import com.google.gson.GsonBuilder
fun main() {
val deserializer = ItemDeserializer()
val gson = GsonBuilder().registerTypeAdapter(ParentItem::class.java, deserializer).create()
val deserializedTest = gson.fromJson(json, FooBarWrapper::class.java)
for (item in deserializedTest.items) {
when (val childProperty = item.childProperty) {
is ChildPropertyFoo -> {
println(childProperty.foo)
println(childProperty.abc)
}
is ChildPropertyBar -> {
println(childProperty.bar)
println(childProperty.ghi)
}
}
}
}然后调试控制台将输出以下内容,您可以看到反序列化代码提供了所需的结果:
This property will be here if itemType is 'foo'
def
This property will be here if itemType is 'bar'
jklhttps://stackoverflow.com/questions/71295744
复制相似问题