Недавно я столкнулся с ситуацией, когда RESTful API предоставил мне список объектов. Обычно это не проблема, поскольку REST предоставляет вам структуру, которую легко преобразовать. Подвох заключался в том, что некоторые объекты внутри других объектов были полиморфными и требовали парсинга определенных полей.
Предыдущее решение состояло в том, чтобы иметь HashMap, содержащую все предоставленные поля. Затем приложению нужно было проверить, какой тип объекта был возвращен, и решить, заполнены ли все необходимые пары «ключ-значение» пригодными для использования данными. Это не было предпочтительным решением и подвержено ошибкам.
Было сделано много предложений по изучению пользовательских (де)сериализаторов, что является хорошей идеей, но также требует много работы. Я наткнулся на скрытый класс RuntimeTypeAdapterFactory в репозитории Gson, который довольно хорошо решил большую часть моей проблемы.
Пример проблемы
Возьмем, к примеру, следующую JSON-структуру:
Как видите, объекты в массиве body
содержат поле с именем type
, которое определяет тип возвращаемого объекта. Это даст вам гибкость для анализа объектов в их собственных уважаемых моделях, которые в данном случае должны быть «text
», «image
» и «video
». См. 3 примера моделей ниже с использованием Google AutoValue.
(Для краткости я не упомянул генераторы Builders и Gson Type Adapter в приведенных выше примерах.)
Решение
Сначала нам нужно создать саму модель статьи. В приведенном ниже примере вы заметите возвращаемый тип List<Block>
. Если вы посмотрите на пример кода Blocks, вы увидите, что все они реализуют интерфейс Block. Таким образом, мы можем легко определить новые типизированные объекты, которые мы хотим поддерживать в будущих обновлениях.
Но теперь разбор его; здесь вмешается магия под названием RuntimeTypeAdapterFactory! При создании парсера Gson вы можете регистрировать всевозможные фабрики адаптеров типов. Добавьте сгенерированный адаптер AutoValue и адаптер среды выполнения, и все готово.
Например:
Всякий раз, когда синтаксический анализатор Gson встречает объект типа Block
, адаптер проверяет, может ли он выполнить синтаксический анализ любого из определенных подтипов. Если ваш API определяет тип в поле, отличном от "type"
, вы можете указать его. "type"
— это имя ключа по умолчанию, поэтому вы можете полностью его опустить, если ваш API работает таким образом.
Предостережения
Я столкнулся с парой мелких проблем, которые мне нужно было решить для моей цели. Эти выводы также могут пригодиться вам, когда вы начнете работать с RuntimeTypeAdapterFactory.
- Если синтаксический анализатор обнаружит непредвиденный тип, будет выдано исключение, и весь ваш объект будет недействителен. Вы можете изменить синтаксический анализатор, чтобы вместо этого он возвращал
null
, если вам не нравится такой подход. - Проанализированное поле
"type"
не будет возвращено в модель — если вам нужна эта информация, вам нужно будет добавить ее повторно.
Удачного кодирования!