У меня есть объявление GsonFactory следующим образом:
public class GsonFactory {
public Gson create() {
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(MyInterface.class, new MyInstanceCreator())
.create();
}
private static final GsonFactory instance = new GsonFactory();
private GsonFactory() {
}
public static Gson getInstance(){
return instance.create();
}
}
У меня также есть класс InstanceCreator:
public class MyInstanceCreator implements InstanceCreator<MyInterface> {
@Override
public StrategySymbol createInstance(Type type) {
return new MyInterfaceImpl();
}
}
class MyClass {
private List<MyInterface> myList;
public List<MyInterface> getMyList() { return myList; }
public void setMyList(List<MyInterface> myList) { this.myList = myList; }
}
interface MyInterface {
Long getValue1();
void setValue1(Long value1);
}
class MyInterfaceImpl implements MyInterface {
private Long value1;
@Override
public Long getValue1() {
return value1;
}
@Override
public void setValue1(Long value1) {
this.value1 = value1
}
}
Этот код кажется хорошо реализованным, но если я попытаюсь разобрать JSON с value1:
MyClass obj = GsonFactory.getInstance().fromJson("{'myList': [{'value1':8}]}", MyClass.class);
Объект возвращается с экземпляром MyInterfaceImpl, но поле value1 всегда пустое. Как я мог видеть, кажется, что Gson ищет поля в interface (нет), а не в class, реализующем interface.
Кто-нибудь знает, как это решить?




Создатель экземпляра может создавать только экземпляры класса, который не определяет конструктор без аргументов. Он не обрабатывает десериализацию. Гсон по-прежнему не сможет определить конкретный тип данных объекта.
Решение. Пользовательский десериализатор для интерфейса
Вам необходимо определить собственный десериализатор для вашего интерфейса и зарегистрировать этот адаптер нового типа на заводе.
public class MyInterfaceDeserializer implements JsonDeserializer<MyInterface> {
@Override
public MyInterface deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return jsonDeserializationContext.deserialize(jsonElement, MyInterfaceImpl.class);
}
}
public class GsonFactory {
public Gson create() {
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(MyInterface.class, new MyInterfaceDeserializer())
.create();
}
}
ИЛИ Общий десериализатор
public class InterfaceDeserializer<T, E> implements JsonDeserializer<T> {
private final Class<E> implementationClass;
public InterfaceDeserializer(Class<E> implementationClass) {
this.implementationClass = implementationClass;
}
@Override
public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return jsonDeserializationContext.deserialize(jsonElement, implementationClass);
}
}
public class GsonFactory {
public Gson create() {
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(MyInterface.class, new InterfaceDeserializer<MyInterface, MyInterfaceImpl>(MyInterfaceImpl.class))
.create();
}
ИЛИ
public class MyInterfaceDeserializer implements JsonDeserializer<MyInterface> {
@Override
public MyInterface deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
MyInterface myInterface = new MyInterfaceImpl();
JsonObject jObject = jsonElement.getAsJsonObject();
myInterface.setValue1(jObject.get("value1").getAsLong());
return myInterface;
}
}
public class GsonFactory {
public Gson create() {
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(MyInterface.class, new MyInterfaceDeserializer())
.create();
}
}
Другой вариант
Используйте RuntimeTypeAdapterFactory, но для этого решения требуется дополнительное свойство type в json для определения точного подтипа, см. документацию.
Пример:
public Gson create() {
RuntimeTypeAdapterFactory<MyInterface> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(MyInterface.class).registerSubtype(MyInterfaceImpl.class, "MyInterfaceImpl");
return new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
}
JSON должен содержать поле типа:
{
myList: [
{
value1: 8,
type: MyInterfaceImpl
}
]
}
Обратите внимание, что для этой опции требуется библиотека gson-дополнения. Этот артефакт находится в репозитории Приложение Cron.
Здравствуйте Евгений, спасибо за такой подробный ответ. Конечно, ``CustomDeserializer` - это то, что нужно.