I make a simple HTTP GET request with retrofit and try to map the json response to my model. The thing is, the json return an array of multiples Shapes, the Shape is an abstract class, so it could be a Square, Circle, etc. Every shape has his own specified model, so different fields. How can I map this array of Shape to the model?
The web service json response
{
"requestId": 0,
"totalShapes": 2,
"shapes": [
{
"circle": {
"code": 1,
"radius": 220
"color" : "blue"
}
},
{
"square": {
"code": 1,
"size": 220
}
}
]
}
Main result mapping :
public class Result {
@SerializedName("requestId") private int requestId;
@SerializedName("totalShapes") private int totalShapes;
@SerializedName("shapes") private List<Shape> shapes;
}
Abstract class :
public abstract class Shape implements Serializable {
}
Circle :
public class Circle {
@SerializedName("code") private int code;
@SerializedName("radius") private int radius;
@SerializedName("color") private String color;
// + getters...
}
Square :
public class Square {
@SerializedName("code") private int code;
@SerializedName("size") private int size;
// + getters...
}
You may implement a custom shape deserializer that acts like a factory. Based on the key for a shape object, you can deserialize it to its corresponding type.
class ShapeDeserializer implements JsonDeserializer<Shape> {
@Override
public Shape deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map.Entry<String, JsonElement> entry = json.getAsJsonObject().entrySet().iterator().next();
switch(entry.getKey()) {
case "circle":
return context.deserialize(entry.getValue(), Circle.class);
case "square":
return context.deserialize(entry.getValue(), Square.class);
default:
throw new IllegalArgumentException("Can't deserialize " + entry.getKey());
}
}
}
Then you register it in your parser
Gson gson =
new GsonBuilder().registerTypeAdapter(Shape.class, new ShapeDeserializer())
.create();
and you use it:
Result result = gson.fromJson(myJson, Result.class);
//Result{requestId=0, totalShapes=2, shapes=[Circle{code=1, radius=220, color='blue'}, Square{code=2, size=220}]}
If the key matches exactly the class name, you might use Class.forName
directly instead (you'd need to capitalize the key in first place).
Also note that:
code
property into the abstract Shape
class if it appears in every subclassesIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With