Reputation: 599
I have a list of objects that I need to deserialize into their proper subclasses. I have been able to get this working by building a gson object like below
JsonDeserializer<Shape> jdTypes = (json, typeOfT, jsonContext) -> {
JsonObject jsonObject = json.getAsJsonObject();
int type = jsonObject.get("type").getAsInt();
Gson gson = new Gson();
switch (type){
case Types.TYPE_CARD:
return gson.fromJson(json, Card.class);
case Types.TYPE_BLOCK:
return gson.fromJson(json, Block.class);
case Types.TYPE_STRIPE:
return gson.fromJson(json, Stripe.class);
//...
}
};
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Shape.class, jdTypes);
builder.create();
Although it is working, there has to be a better way of doing it. Currently I am creating an instance of Gson within my GsonBuilder. Yikes. Is there a way I can use the Gson object I am about to build to deserialize the different sub-types?
Upvotes: 0
Views: 200
Reputation: 21145
No need to reinvent the wheel. Your problem is classic in the scope of Gson, and there is a special RuntimeTypeAdapterFactory
that is designed to solve problems of exactly such a kind. For example, if you have something like this:
final class Types {
private Types() {
}
static final int TYPE_CARD = 1;
static final int TYPE_BLOCK = 2;
static final int TYPE_STRIPE = 3;
abstract static class Abstract {
private Abstract() { }
static final class Card extends Abstract { }
static final class Block extends Abstract { }
static final class Stripe extends Abstract {}
}
}
You can configure and use RuntimeTypeAdapterFactory
:
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(
RuntimeTypeAdapterFactory.of(Types.Abstract.class, "type")
.registerSubtype(Types.Abstract.Card.class, String.valueOf(Types.TYPE_CARD))
.registerSubtype(Types.Abstract.Block.class, String.valueOf(Types.TYPE_BLOCK))
.registerSubtype(Types.Abstract.Stripe.class, String.valueOf(Types.TYPE_STRIPE))
)
.create();
Then you don't even need anything else:
private static final Type abstractListType = new TypeToken<List<Types.Abstract>>() {
}.getType();
public static void main(final String... args) {
gson.<List<Types.Abstract>>fromJson("[{\"type\":1},{\"type\":2},{\"type\":3}]", abstractListType)
.stream()
.map(Types.Abstract::getClass)
.forEach(System.out::println);
}
Output:
class q43159721.Types$Abstract$Card
class q43159721.Types$Abstract$Block
class q43159721.Types$Abstract$Stripe
Upvotes: 1
Reputation: 39873
If I understand this issue correctly, you could just use the JsonDeserializationContext
directly.
return jsonContext.deserialize(json, classOfType(type));
Where classOfType()
should just return the the class corresponding to the type
of your shape.
Upvotes: 1