Suppose we have this JSON:
[
{
"__typename": "Car",
"id": "123",
"name": "Toyota Prius",
"numDoors": 4
},
{
"__typename": "Boat",
"id": "4567",
"name": "U.S.S. Constitution",
"propulsion": "SAIL"
}
]
(there could be many more elements to the list; this just shows two)
I have Car
and Boat
POJOs that use a Vehicle
base class for the common fields:
public abstract class Vehicle {
public final String id;
public final String name;
}
public class Car extends Vehicle {
public final Integer numDoors;
}
public class Boat extends Vehicle {
public final String propulsion;
}
The result of parsing this JSON should be a List<Vehicle>
. The problem is that no JSON parser is going to know, out of the box, that __typename
is how to distinguish a Boat
from a Car
.
With Gson, I can create a JsonDeserializer<Vehicle>
that can examine the __typename
field, identify whether this is a Car
or Boat
, then use deserialize()
on the supplied JsonDeserializationContext
to parse the particular JSON object into the appropriate type. This works fine.
However, the particular thing that I am building ought to support pluggable JSON parsers, and I thought that I would try Moshi as an alternative parser. However, this particular problem is not covered well in the Moshi documentation at the present time, and I am having difficulty figuring out how best to address it.
The closest analogue to JsonDeserializer<T>
is JsonAdapter<T>
. However, fromJson()
gets passed a JsonReader
, which has a destructive API. To find out what the __typename
is, I would have to be able to parse everything by hand from the JsonReader
events. While I could call adapter()
on the Moshi
instance to try to invoke existing Moshi parsing logic once I know the proper concrete type, I will have consumed data off of the JsonReader
and broken its ability to provide the complete object description anymore.
Another analogue of JsonDeserializer<Vehicle>
would be a @FromJson
-annotated method that returns a Vehicle
. However, I cannot identify a simple thing to pass into the method. The only thing that I can think of is to create yet another POJO representing the union of all possible fields:
public class SemiParsedKindOfVehicle {
public final String id;
public final String name;
public final Integer numDoors;
public final String propulsion;
public final String __typename;
}
Then, in theory, if I have @FromJson Vehicle rideLikeTheWind(SemiParsedKindOfVehicle rawVehicle)
on a class that I register as a type adapter with Moshi
, Moshi might be able to parse my JSON objects into SemiParsedKindOfVehicle
instances and call rideLikeTheWind()
. In there, I would look up the __typename
, identify the type, and completely build the Car
or Boat
myself, returning that object.
While doable, this is a fair bit more complex than the Gson approach, and my Car
/Boat
scenario is on the simple end of the possible data structures that I will need to deal with.
Is there another approach to handling this with Moshi that I am missing?