2015-02-06 5 views
1

Если я зарегистрировать JsonSerializer с интерфейсом или абстрактный класс как:Gson сериализовать интерфейс/абстрактный класс

GsonBuilder builder = new GsonBuilder() 
    .registerTypeAdapter(MyInterface.class, new MySerializer()); 
Gson gson = builder.create(); 

MyInterface i = new MyConcreteClass(); 
String json = gson.toJson(i); // <--- Serializer is not invoked 

сериализатора просто не вызывается в gson.toJson().

Однако, если у меня есть класс, который содержит MyInterface:

public class MyAnotherClass { 
    MyInterface i; 
    // ... 
} 

MyAnotherClass c = new MyAnotherClass() 
String json = gson.toJson(c); // <--- Serializer is invoked 

сериализатора вызывается.

Для меня это своего рода непоследовательное поведение. Способ, которым он работает на переменную-члене, - это искать тип вместо фактического типа, но способ, которым он работает на «корневом» объекте, отличается.

Это ошибка/дефект или ожидаемое поведение? Если это ожидается, любая причина?

ответ

5

Это ожидаемое поведение. У Gson есть список объектов по умолчанию TypeAdapterFactory. Вы можете видеть их в source of the Gson class.

Есть адаптеры для String, Number, Map, Collection ... Последний является важным и используются, когда никакие переходники не могут обрабатывать тип: это ReflectiveTypeAdapter. Он извлекает путем отражения все поля объекта для сериализации и ищет соответствующие адаптеры для сериализации значений полей.

Итак, когда Gson должен сериализовать объект, сначала он пытается найти адаптер, используя фактический тип объекта. Если найденный адаптер не является ReflectiveTypeAdapter, он использует его. В противном случае он ищет адаптер с декларативным типом и использует его, если это не ReflectiveTypeAdapter. В противном случае он использует ReflectiveTypeAdapter на объекте (вы можете посмотреть на источник класса TypeAdapterRuntimeTypeWrapper, чтобы получить более подробную информацию об этом mecanism). Это поведение, которое у вас есть с вашим атрибутом MyInterface i.

Когда вы делаете:

MyInterface i = new MyConcreteClass(); 
String json = gson.toJson(i); 

Gson пытается найти адаптер для типа MyConcreteClass и он находит ReflectiveTypeAdapter. Теперь он должен искать адаптер с декларативным типом. Но он не имеет возможности узнать декларативный тип, потому что ни один объект не ссылается на него, это корневой объект. Вы знаете в своем коде, что хотите использовать тип MyInterface, но Gson не имеет этой информации и имеет только экземпляр объекта. Вы можете подумать, что Gson мог видеть, что объект реализует MyInterface, но это не так, как он работает (и что делать, если объект реализует два интерфейса?)

Итак, у вас есть два пути, чтобы решить вашу проблему:

1) При вызове Gson.toJson, вы можете дать декларативного типа для корневого объекта: Gson#toJson(Object, Type)

String json = gson.toJson(i, MyInterface.class); 

2) Когда вы зарегистрировать адаптер, вы можете указать, что этот адаптер относится ко всем подклассам: GsonBuilder#registerTypeHierarchyTree

GsonBuilder builder = new GsonBuilder() 
    .registerTypeHierarchyAdapter(MyInterface.class, new MySerializer()); 

Надеется, что это помогает

+0

Спасибо за ответ. Тем не менее, я думаю, что это ожидаемый * дефект * из-за ограничения Java. –

+0

Когда я использую registerTypeHierarchyAdapter Я застреваю в непрерывном цикле, когда метод serialize выполняет result.add («properties», context.serialize (src, src.getClass())) –