Gson 和使用继承序列化对象的 ArrayList

Gson and Serializing an ArrayList of Objects with Inheritance

我对 Gson 和 Json 很陌生。我有一些简单的事件,我想在 Gson 的帮助下通过 Json 进行序列化。

注意:Kotlin 中的代码。

public abstract class Event() {
}

public class Move : Event() {
        var from: Point? = null
        var to: Point? = null
    }

    public class Fire : Event() {
        var damage: Int = 0
        var area: ArrayList<Point> = ArrayList(0)
    }

    public class Build : Event() {
        var to: Point? = null
        var type: String = ""
        var owner: String = ""
    }

我通过这种方式坚持了一堆:

val list: ArrayList<Event>() = ArrayList()
list.add(move)
list.add(fire)
val str = gson.toJson(events)

并坚持:

val type = object : TypeToken<ArrayList<Event>>(){}.getType()
val eventStr = obj.getString("events")
val events: ArrayList<Event> = gson.fromJson(eventStr, type)

我已经尝试过为 Event-class 创建序列化器和反序列化器,并通过 registerTypeAdapter 注册它,我也尝试过 RuntimeTypeAdapterFactory,但都不会保留取消保留正确类型所需的信息。

例如,RuntimeTypeAdapterFactory 表示: "cannot deserialize Event because it does not define a field named type"

编辑:这是 "Adapter" 的代码,它是..改编自另一个 Whosebug post:

public class Adapter :
            JsonSerializer<Event>,
            JsonDeserializer<Event> {

        final val CLASSNAME = "CLASSNAME"
        final val INSTANCE  = "INSTANCE"

        override fun serialize(src: Event?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement? {
            val obj = JsonObject()
            val className = (src as Event).javaClass.getCanonicalName()
            obj.addProperty(CLASSNAME, className)
            val elem = context!!.serialize(src)
            obj.add(INSTANCE, elem)
            return obj
        }

        override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Event? {
            val jsonObject =  json!!.getAsJsonObject()
            val prim = jsonObject.get(CLASSNAME)
            val className = prim.getAsString()
            val klass = Class.forName(className)
            return context!!.deserialize(jsonObject.get(INSTANCE), klass)
        }
    }

此代码失败并出现 NullPointerException:

val className = prim.getAsString()

你不能这样做。

您提到的示例不针对您的案例。它仅适用于一种情况:如果您注册基类型(而不是类型层次结构)并使用 gson.toJson(obj, javaClass<Event>()) 进行序列化。它永远不会对数组起作用,除非你也为你的事件容器对象编写自定义序列化程序

通常您需要另一种方法:使用 TypeAdapterFactory 和委托适配器:GSON: serialize/deserialize object of class, that have registered type hierarchy adapter, using ReflectiveTypeAdapterFactory.Adapter and https://code.google.com/p/google-gson/issues/detail?id=43#c15

我认为这种方法过于复杂,所以如果您的类型很少,最简单的解决方案是通过自定义序列化程序逐个字段手动序列化这些类型,而忘记尝试委托给默认值