Reputation: 92
I have a abstract class as given below
public abstract class IndexedPojo {
List<String> keySet;
public List<String> getKeySet() {
return keySet;
}
public void setKeySet(List<String> keySet) {
this.keySet = keySet;
}
public void setKeySet(JsonObject parameters) {
this.keySet = new ArrayList<>(parameters.keySet());
}
public void setKeySet(Map<String,String> parameters) {
this.keySet = new ArrayList<>(parameters.keySet());
}
}
The purpose of this abstract class is to provide functionality of keeping track of variables that has been initialized in the classes that extend this. Why I need this? I am modelling pojo class for forms to be used in Test automation project. Where some parameters are optional and if they have not been initialized corresponding form elements are not filled. Currently I am using this method to create instance of the classes that extend IndexedPojo
class:
public static <T extends IndexedPojo> T deserializeJsonTo(JsonObject parameters, Class<T> tClass) {
Gson gson = new Gson();
T instance = gson.fromJson(parameters, tClass);
instance.setKeySet(parameters);
return instance;
}
But now the problem is some of the nested children are also classes that extend IndexedPojo
and I want to initialize them in the same way. For example
public class RuleSetConfigForm extends IndexedPojo {
@SerializedName("Key")
SomeNestedConfig nestedConfig;
}
public class SomeNestedConfig extends IndexedPojo {
@SerializedName("Some Key")
private String someOptionalParamater1 = "";
@SerializedName("Some Key2")
private String someOptionalParamater2 = "";
@SerializedName("Some Key3")
private String someOptionalParamater3 = "";
}
How can I initialize the nested SomeNestedConfig nestedConfig
as I initialized the RuleSetConfigForm
? Is there any automated way of doing this?
Upvotes: 0
Views: 1442
Reputation: 6934
This can be solved using a custom TypeAdapterFactory
which delegates to the default adapter and afterwards calls your IndexedPojo.setKeySet(...)
method:
class IndexedPojoTypeAdapterFactory implements TypeAdapterFactory {
public static final IndexedPojoTypeAdapterFactory INSTANCE = new IndexedPojoTypeAdapterFactory();
private IndexedPojoTypeAdapterFactory() { }
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Only handle IndexedPojo and subclasses
if (!IndexedPojo.class.isAssignableFrom(type.getRawType())) {
return null;
}
// Get the default adapter as delegate
// Cast is safe due to `type` check at method start
@SuppressWarnings("unchecked")
TypeAdapter<IndexedPojo> delegate = (TypeAdapter<IndexedPojo>) gson.getDelegateAdapter(this, type);
// Cast is safe because `T` is IndexedPojo or subclass (due to `type` check at method start)
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new TypeAdapter<IndexedPojo>() {
@Override
public void write(JsonWriter out, IndexedPojo value) throws IOException {
delegate.write(out, value);
}
@Override
public IndexedPojo read(JsonReader in) throws IOException {
// Read JsonObject from JsonReader to be able to pass it to `IndexedPojo.setKeySet(...)`
// afterwards
// Note: JsonParser automatically parses in lenient mode, which cannot be disabled
// Note: Might have to add handling for JSON null values
JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject();
IndexedPojo value = delegate.fromJsonTree(jsonObject);
value.setKeySet(jsonObject);
return value;
}
};
return adapter;
}
}
And then use this factory in your deserializeJsonTo(...)
method:
public static <T extends IndexedPojo> T deserializeJsonTo(JsonObject parameters, Class<T> tClass) {
// Note: Could also store Gson instance in `static final` field
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(IndexedPojoTypeAdapterFactory.INSTANCE)
.create();
return gson.fromJson(parameters, tClass);
}
Upvotes: 2