#java #json #gson #deserialization #pojo
#java #json #gson #десериализация #pojo
Вопрос:
У меня есть абстрактный класс, как указано ниже
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());
}
}
Цель этого абстрактного класса — обеспечить функциональность отслеживания переменных, которые были инициализированы в классах, которые расширяют это. Зачем мне это нужно? Я моделирую класс pojo для форм, которые будут использоваться в проекте автоматизации тестирования. Где некоторые параметры являются необязательными, и если они не были инициализированы, соответствующие элементы формы не заполняются. В настоящее время я использую этот метод для создания экземпляра классов, расширяющих IndexedPojo
класс:
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;
}
Но теперь проблема в том, что некоторые из вложенных дочерних элементов также являются классами, extend IndexedPojo
и я хочу инициализировать их таким же образом. Например
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 = "";
}
Как я могу инициализировать вложенный SomeNestedConfig nestedConfig
, когда я инициализировал RuleSetConfigForm
? Есть ли какой-либо автоматизированный способ сделать это?
Ответ №1:
Это можно решить с помощью пользовательского TypeAdapterFactory
, который делегирует адаптер по умолчанию, а затем вызывает ваш IndexedPojo.setKeySet(...)
метод:
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;
}
}
А затем используйте эту фабрику в своем deserializeJsonTo(...)
методе:
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);
}
Комментарии:
1. Это работает идеально. Благодаря вам мой код стал намного более гибким