Search code examples
javaandroidjsongenson

Order matters with class metadata in Genson - Is there a work-around?


I'm using Genson to serialize + deserialize json in my android app into polymorphic objects. The JSON is coming from a variety of sources though and I can't guarantee that the @class metadata will be the first line item in the json. Walking through the Genson code and writing test cases it looks like the @class metadata has to be the first entry in the dictionary.

Has anyone had luck working around this constraint? Is it time to switch to something else, and if so, what?

public class Message {
  Payload payload;
  // getters & setters
}
public abstract class Payload {
  //
}
public class Notification1 extends Payload {
  String text;
  // getters & setters
}
public class Notification2 extends Payload {
  String otherText
  // getters & setters
}

String correctOrder = {"@class":"Message","payload":{"@class":"Notification1","text":"Text"}}
String modifiedOrder = {"@class":"Message","payload":{"text":"Text", "@class":"Notification1"}}

Genson g = Genson.Builder()
            .addAlias("Notification1", Notification1.class)
            .addAlias("Notification2", Notification2.class)
            .useRuntimeType(true)
            .useClassMetadata(true)
            .useMetadata(true)
            .useFields(false)
            .useIndentation(false)
            .create();

g.deserialize(correctOrder, Message.class) // This works
g.deserialize(modifiedOrder, Message.class) // This barfs with the error: com.owlike.genson.JsonBindingException: Could not deserialize to type class com.ol.communication.messages.Message

Solution

  • Indeed the order matters. This was choosed on purpose, see the remarks in the user guide.

    If we allow the @class property anywhere in the json object, then we will have to first deserialize all the json object (and its sub properties obj/arr etc) to an intermediary data structure and then to the correct type. This would incur additional memory overhead and less speed but greater flexibility, true.

    A solution would be to mark classes that are polymorphic (annotation/config in the builder), for whom Genson would search/produce the @class property in the stream. This would allow to have this overhead only for the polymorphic objects in the stream.

    At the moment it is not implemented, but I opened an issue. It will come in a future release.

    Outside of the technical aspects, I don't think you should have polymorphic logic (or any other fancy stuff) when you are dealing with multiple external API. I mean this kind of features is library specific, so if you don't use the same tool on both sides you can run into troubles. Usually people have a layer that will be used to communicate with the APIs and map the data to YOUR model. If you don't own the code on both ends, I think this would be a good solution on the long term.