1

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()
user38725
  • 876
  • 1
  • 10
  • 26
  • I suppose creating a custom deserializer is the way to go. Could you post the code you wrote? – rciovati Jul 02 '15 at 17:34
  • @RiccardoCiovati Hi, I added some code. No problems whatsovever in serializing the Events.. however, like I said, when I deserialize 'em, the information simply isn't there (I println'ed a deserialized Move-event, and it only had the Point data of "from" and "to") (I have a custom serializer for Point-class too) – user38725 Jul 02 '15 at 19:22
  • Silly me. I wasn't registering the type adapter for the sub-classes at all, but only for the Event-class, that was the problem. However, now I am having a StackOverflowError on the line: `val elem = context!!.serialize(src)`. Any ideas what might cause this? – user38725 Jul 02 '15 at 19:37
  • I _suspect_ it is due to the `(src as Event)`. But I'm not sure how to fix it. – user38725 Jul 02 '15 at 19:46
  • You have stack overflow because you call serialize on src object that is already serializing.. You just call your code recursively – Sergey Mashkov Jul 03 '15 at 08:27
  • I am using the code found here: http://stackoverflow.com/a/8683689/4672864. The comments in the post also say that if I use an _interface_, I shouldn't have the infinite recursion problem. I don't quite understand why.. and I tried to change the event from abstract class to Interface, but I still get the infinite recursion. – user38725 Jul 03 '15 at 09:01
  • @fluxi you get stack overflow because of call context.serialize and registerTypeHierarchy-registration. `src as Event` is irrelevant, you even don't need it – Sergey Mashkov Jul 03 '15 at 12:45

1 Answers1

1

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

Community
  • 1
  • 1
Sergey Mashkov
  • 4,630
  • 1
  • 27
  • 24
  • Thanks for your answer. I solved it by manually serializing the types, and simply having a separate ArrayList for each type. – user38725 Jul 04 '15 at 14:37