Recently, I ran into a situation where a RESTful API provided me with a list of objects. Normally this isn’t a problem since REST provides you with a structure that is easy to convert. The catch was that some objects inside other objects were polymorphic and required to parse specific fields.
The previous solution was to have a HashMap that contained all the supplied fields. The application then needed to check what kind of object was returned and decide if all required key-value pairs were filled with usable data. This was not a preferred solution and prone to errors.
A lot of suggestions were made to look into custom (de)serializers, which is a good idea, but also a lot of work. I came across the “hidden” RuntimeTypeAdapterFactory class in the Gson repository which solved most of my problem quite nicely.
An Example Problem
Take the following JSON-structure for example:
As you can see, the objects in the
body array contain a field called
type, which determines the type of object being returned. This will give you flexibility to parse the objects into their own respected models, which in this case should be “
image” and “
video”. See the 3 model examples below using Google’s AutoValue.
(For brevity sake I left out the Builders and Gson Type Adapter generators in the above examples.)
First we’ll need to create the Article model itself. In the example below you’ll notice the
List<Block> return type. If you take a look at the Blocks example code, you’ll see these all implement the Block interface. This way we can easily define new typed objects we want to support in future updates.
But now parsing it; here the magic called RuntimeTypeAdapterFactory will step in! When creating the Gson parser, you can register all kinds of type adapter factories. Add the generated AutoValue adapter and the Runtime adapter and you’re set.
Whenever the Gson parser encounters an object of the type
Block the adapter will check if it can parse to any of the defined subtypes. If your API defines the type in a different field than
"type" you can provide it.
"type" is the default name for the key so you can omit it entirely if your API does work this way.
I ran into a couple of minor issues which I needed to solve for my purpose. These findings might also come in handy for you to know when you’ll start working with the RuntimeTypeAdapterFactory.
- If the parser encounters an unexpected type, an exception will be thrown and your complete object will be invalid — You can modify the parser to return
nullinstead if you don’t like this approach
- The parsed
"type"field will not be returned to the model — if you want this info, you’ll need to re-add it