I'm trying to implement some simple Json serialization functionality but I'm having a hard time coping with the massive complexity of Gson.
So basically I have a bunch of Entity classes which reference each other with a lot of circular reference. To serialize this structure to JSON I want to keep track of the objects already serialized. The Entity classes all implement an interface called Identified
which has one method String getId()
giving a globally unique id. So during serializiation of one root element, I want to store all encountered ids in a Set
and decide based on that set, whether to fully serialize an object or to serialize that object as a stub
"something": {
"__stub": "true",
"id": "..."
}
This shouldn't be too hard a task in my opinion, but I haven't been able to put something together. Using a custom JsonSerializer
I'm not able to have an object (that is not to be serialized as a stub) serialized in the default way. Using a TypeAdapterFactory
, I'm not able to access the actual object.
So, any help on how to achieve this, would be very nice!
Best regards
I'm not sure if it's possible easily. As far as I know, Gson promotes immutability and seems to lack custom serialization context support (at least I don't know if it's possible to use custom
JsonSerializationContext
wherever possible). Thus, one of possible work-around might be the following:IIdentifiable.java
A simple interface to request a custom ID for an object.
Entity.java
A simple entity that can hold another entity references in two manners:
IdentitySerializingTypeAdapterFactory.java
I didn't find any easier way rather than making it a type adapter factory, and, unfortunately, this implementation is totally stateful and cannot be reused.
The type adapter firstly tries to check if a given entity has been already traversed. If yes, then it's writing a special object similar to your one (the behavior could be rewritten via the strategy pattern, of course, but let it be more simple). If no, then the default type adapter is obtained, and then the given entity is delegated to that adapter, and registered as a traversed one if the latter type adapter succeeds.
The rest
And here is the rest.
SystemNames.java
GsonJsonWriters.java
Testing
In the test below, there are three entities identified by
FOO
,BAR
, andBAZ
string identifiers. All of them have circular dependencies like this:FOO
->BAR
,BAR
->BAZ
,BAZ
->FOO
using thenext
property;FOO
->[BAR, BAZ]
,BAR
->[FOO, BAZ]
,BAZ
->[FOO, BAR]
using theentities
property.Since the type adapter factory is stateful, even
GsonBuilder
must be created from scratch thus not having "spoiled" state between use. Simply speaking, once a Gson instance is used once, it must be disposed, so there areGsonBuilder
suppliers in the test below.Deserialization of such stuff looks really complicated. At least using GSON facilities.
Do you consider rethinking your JSON model in order to avoid circular dependencies in JSON output? Maybe decomposing your objects to a single map like
Map<ID, Object>
and making references transient or@Expose
-annotated could be easier for you to use? It would simplify deserialization as well.