4

I'm currently having an issue with the deserialization of certain inner-objects, in spring, I initialize all of my objects before outputting them using @ResponseBody.

As an example, this is a response:

[{id:1, location:{id:1, ... extra location data}},
 {id:2, location:1}
]

Now, GSON throws an error as it is not able to understand that location:1 refers to the location object already deserialized in the previous object. Deserialization is done in the following method:

@Override
public void handleReader(Reader reader) {
    try {
        String json = readerToString(reader);
        T object = getGson().fromJson(json, returnType);
        handleObject(object);
    } catch (Exception e) {
        Sentry.captureException(e);
    }
}

As an example, this is called through a regular generic class, I'd use the type Event[] as the T generic in order to return an array.

How can I either fix this using Gson or make spring output the full data every time? Ideally I'd like to fix with Gson as it would allow for seriously reduced bandwidth but I'm not too fussed at this point.

My Spring returning method is as follows:

@Override
public List<T> list() {
    return service.findAll();
}

with the initialization like so:

@Override
@Transactional
public List<Event> findAll() {
    List<Event> list = eventRepository.findByArchivedFalse();
    for (Event event : list) {
        this.initialize(event);
    }
    return list;
}
@Override
public Event initialize(Event obj) {
    Hibernate.initialize(obj.getLocation());
    Hibernate.initialize(obj.getLocation().get... inner data here);
    return obj;
}

I imagine this is going to require a real structure review but, if I can help it, I'd like to keep the structure roughly the same.

durron597
  • 31,968
  • 17
  • 99
  • 158
Thomas Nairn
  • 1,186
  • 8
  • 34

1 Answers1

2

You're going to have to write a custom deserializer, if you're not willing to change the JSon. However, changing the JSon is exactly what I would recommend.

Option 1: Changing the JSon

I think the right thing to do is to have two separate messages, e.g.

{
  "uniqueLocations":
    [
      {"id":1, ... extra location details} ,
    ],
  "locationMap":
    [ 
      {"id":1,"location":1},
      {"id":2,"location":1}
      ... etc.
    ]
}

This is clearer; this separates your json so that you always have the same types of data in the same places.


Option 2: Making Gson able to do more complicated deserializations

However, if you're not willing to do that, you could write a custom deserializer. The most straightforward way to do that, extending TypeAdapter, only uses specific, concrete classes, not parameterized types. However, if you want to use a parameterized type, you must use a TypeAdapterFactory.

You can read more about how to do this here: How do I implement TypeAdapterFactory in Gson?

Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • I would love to change the JSON, unfortunately it would mean changing the Models of over 3 different platforms which isn't really an option right now. I think if it's not automatic within Gson, I'll be looking at getting Spring to just give full data (As horrible as that sounds) Thanks man! – Thomas Nairn May 01 '15 at 14:21
  • @ThomasNairn That's what the second part of my answer is talking about, how to make Gson do it automatically – durron597 May 01 '15 at 14:22
  • Hmm, it's just that that in itself is going to require maybe half a days work across 2 of the platforms which isn't really viable right now, I think if I'm going to spend that long, I may as well change the structure of the json which again, it's currently possible with the time constraints I have at the moment. Again, thank you for your input. – Thomas Nairn May 01 '15 at 14:31