I want to deserialize json objects to specific types of objects (using Gson library) based on type
field value, eg.:
[
{
"type": "type1",
"id": "131481204101",
"url": "http://something.com",
"name": "BLAH BLAH",
"icon": "SOME_STRING",
"price": "FREE",
"backgroundUrl": "SOME_STRING"
},
{
....
}
]
So type
field will have different (but known) values. Based on that value I need to deserialize that json object to appropriate model object, eg.: Type1Model, Type2Model etc.
I know I can easily do that before deserialization by converting it to JSONArray
, iterate through it and resolve which type it should be deserialized to. But I think it's ugly approach and I'm looking for better way. Any suggestions?
You may implement a JsonDeserializer
and use it while parsing your Json value to a Java instance. I'll try to show it with a code which is going to give you the idea:
1) Define your custom JsonDeserializer
class which creates different instance of classes by incoming json value's id property:
class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> { @Override public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); JsonElement jsonType = jsonObject.get("type"); String type = jsonType.getAsString(); MyBaseTypeModel typeModel = null; if("type1".equals(type)) { typeModel = new Type1Model(); } else if("type2".equals(type)) { typeModel = new Type2Model(); } // TODO : set properties of type model return typeModel; } }
2) Define a base class for your different instance of java objects:
class MyBaseTypeModel { private String type; // TODO : add other shared fields here }
3) Define your different instance of java objects' classes which extend your base class:
class Type1Model extends MyBaseTypeModel { // TODO: add specific fields for this class } class Type2Model extends MyBaseTypeModel { // TODO: add specific fields for this class }
4) Use these classes while parsing your json value to a bean:
GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer()); Gson gson = gsonBuilder.create(); MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);
I can not test it right now but I hope you get the idea. Also this link would be very helpful.
@stephane-k 's answer works, but it is a bit confusing and could be improved upon (see comments to his answer)
Copy https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java into your project. (It's ok; these classes are designed to be copy/pasted https://github.com/google/gson/issues/845#issuecomment-217231315)
Setup model inheritance:
// abstract is optional abstract class BaseClass { } class Type1Model extends BaseClass { } class Type2Model extends BaseClass { }
Setup GSON or update existing GSON:
RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory .of(BaseClass.class, "type") .registerSubtype(Type1Model.class, "type1") .registerSubtype(Type2Model.class, "type2"); Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory) .create();
Deserialize your JSON into base class:
String jsonString = ... BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);
baseInstance
will be instanceof either Type1Model
or Type2Model
.
From here you can either code to an interface or check instanceof and cast.
use https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
then configure it with
public static final class JsonAdapterFactory extends
RuntimeTypeAdapterFactory<MediumSummaryInfo> {
public JsonAdapterFactory() {
super(MyBaseType.class, "type");
registerSubtype(MySubtype1.class, "type1");
registerSubtype(MySubtype2.class, "type2");
}
}
and add the annotation:
@JsonAdapter(MyBaseType.JsonAdapterFactory.class)
to MyBaseType
Much better.
If you have a lot of sub types and you do not want to or cannot maintain a list of them, you can also use an annotation based approach.
Here is the required code and also some usage examples: https://gist.github.com/LostMekka/d90ade1fe051732d6b4ac60deea4f9c2 (it is Kotlin, but can easily be ported to Java)
For me, this approach is especially appealing, since I write a small library that does not know all possible sub types at compile time.
If 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