I have a class that looks as follows
class Person {
Long id;
String firstName;
int age;
}
and my input either looks like this:
{ "id": null, "firstName": "John", "age": 10 }
or like this:
{ "id": 123 }
The first variant represents a "new" (non-persisted) person and the second refers to a person by its database id.
If id
is non-null, I would like to load the object from database during deserialization, otherwise fallback on regular parsing and deserialize it as a new object.
What I've tried: I currently have a JsonDeserializer
for database-deserialization, but as I understand it, there is no way to "fall back" on regular parsing. According to this answer I should use a TypeAdapterFactory
and the getDelegateAdapter
. My problem with this approach is that I'm given a JsonReader
(and not for instance a JsonElement
) so I can't determine if the input contains a valid id
without consuming input.
Any suggestions on how to solve this?
I think I managed to figure this out with the help of the answer over here.
Here is a working type adapter factory:
new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter =
gson.getAdapter(JsonElement.class);
// Are we asked to parse a person?
if (!type.getType().equals(Person.class))
return null;
return new TypeAdapter<T>() {
@Override
public T read(JsonReader reader) throws IOException {
JsonElement tree = elementAdapter.read(reader);
JsonElement id = tree.getAsJsonObject().get("id");
if (id == null)
return delegate.fromJsonTree(tree);
return (T) findObj(id.getAsLong());
}
@Override
public void write(JsonWriter writer, T obj) throws IOException {
delegate.write(writer, obj);
}
};
}
}
I haven't fully tested it yet and I'll get back and revise it if needed. (Posting it now to open up for feed back on the approach.)