Я написал JsonDeserializer
для сопоставления с подклассами, прочитав тип подкласса из json.Это прекрасно работает, за исключением случаев, когда тип является вложенным, когда тип BaseClass
содержит BaseClass
.Когда это происходит, десериализатор никогда не вызывается для вложенного класса.Я подал отчет об ошибке , но хочу подтвердить, что это действительно ошибка, или посмотреть, есть ли способ обойти эту проблему.
Полный пример прилагается.Gson версия 2.8.5.Котлин версия 1.3.10
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import java.lang.reflect.Type
open class BaseClass {
var myType: String? = null
var myList = mutableListOf<BaseClass>()
var dep: BaseClass? = null
}
open class ClassA : BaseClass() {
val astuff: String? = null
}
open class ClassB : BaseClass() {
val bstuff: String? = null
}
open class Resources {
val myList = mutableListOf<BaseClass>()
}
val json1 = """
{
"myList": [
{
"myType": "ClassA",
"astuff": "a stuff"
},
{
"myType" : "ClassB",
"bstuff" : "things b needs"
}
]
}
""".trimIndent()
val json2 = """
{
"myType" : "ClassA",
"astuff" : "a stuff",
"myList" : [
{
"myType" : "ClassB",
"bstuff" : "things b needs"
}
]
}
""".trimIndent()
val json3 = """
{
"myType" : "ClassA",
"astuff" : "a stuff",
"dep" : {
"myType" : "ClassB",
"bstuff" : "things b needs"
},
"myList" : [
{
"myType" : "ClassB",
"bstuff" : "things b needs"
}
]
}
""".trimIndent()
fun main(args: Array<String>) {
val gson = getGson()
// working example
val test1 = gson.fromJson(json1, Resources::class.java)
if (!(test1.myList[0] is ClassA)) {
println("Test1 Error: first array entry should be ClassA but is ${test1.myList[0]::class.java}")
}
if (!(test1.myList[1] is ClassB)) {
println("Test1 Error: first array entry should be ClassB but is ${test1.myList[1]::class.java}")
}
// broken examples
val test2 = gson.fromJson(json2, BaseClass::class.java)
if (!(test2.myList[0] is ClassA)) {
println("Test3 Error: first array entry should be ClassA but is ${test2.myList[0]::class.java}")
}
val test3 = gson.fromJson(json3, BaseClass::class.java)
}
fun getGson(): Gson {
return GsonBuilder()
.registerTypeAdapter(BaseClass::class.java, ClassDeserializerAdapter1<BaseClass>("myType"))
.create()
}
class ClassDeserializerAdapter1<T> internal constructor(private val typeName: String) : JsonDeserializer<T> {
private val gson: Gson
init {
gson = GsonBuilder()
.create()
}
@Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): T {
val jsonObject = json.asJsonObject
val typeElement = jsonObject.get(typeName)
val method = typeElement.asString
val classType = Class.forName("com.juicelabs.fhir.base.$method") as Class<out T>
return gson.fromJson(json, classType)
}
}