user38725
user38725

Reputation: 901

Gson and Serializing an ArrayList of Objects with Inheritance

I am very new to Gson and Json. I have simple Events that I want to serialize through Json with the help of Gson.

Note: Code in 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 = ""
    }

I am persisting bunch of these via this way:

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

And unpersisting:

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

I have tried both creating a serializer & deserializer for Event-class, and registering it via registerTypeAdapter, and I have also tried the RuntimeTypeAdapterFactory, but neither will persist the information required to unpersist the correct type.

For example, the RuntimeTypeAdapterFactory says: "cannot deserialize Event because it does not define a field named type"

EDIT: Here's the code for the "Adapter", which was.. well, adapted from another StackOverflow 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)
        }
    }

This code fails with NullPointerException on line:

val className = prim.getAsString()

Upvotes: 1

Views: 2081

Answers (1)

Sergey Mashkov
Sergey Mashkov

Reputation: 4760

You can't do it this way.

The example you are referring is not targeted to your case. It works in only one case: if you register base type (not type hierarchy) and serialize using gson.toJson(obj, javaClass<Event>()). It will never work for array except you write custom serializer for you events container object too

Generally you need another approach: use TypeAdapterFactory and delegate adapters: 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

I believe this approach is overcomplicated so if you have few types the easiest solution is two serialize these types by hand, field by field via custom serializer and forget about attempts to delegate to default

Upvotes: 1

Related Questions