简体   繁体   中英

Spring JSON serialization, Gson deserialization

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.

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, eg

{
  "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?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM