#lift-json
#lift-json
Вопрос:
У нас есть существующий класс A
, который ранее был единственным типом, который мог отображаться в определенной позиции в выводе JSON нашей конечной точки REST API. Однако теперь я расширяю общий базовый класс B
, чтобы в этой позиции на выходе мог отображаться диапазон различных типов, все из которых наследуются B
.
Я добавил подсказку типа в Formats
, но, несмотря Formats
на то, что методы корректно просматривали информацию подсказки типа в обоих направлениях, подсказки типа игнорировались при сериализации с помощью lift-json.
Оказалось, что причина, по которой lift-json не добавлял поля подсказки типа в JSON, заключалась в том, что в нашем экземпляре также был настроен пользовательский сериализатор для этого класса Formats
, а пользовательские сериализаторы переопределяют подсказки типа.
Итак, как мы можем создать класс, который одновременно имеет пользовательский сериализатор, а также выдает и создает подсказки типа, позволяющие однозначно идентифицировать его тип (как на клиентах, так и на серверах)?
Ответ №1:
Это уже не очень хорошо документировано, но у этой TypeHints
черты есть два метода:
def deserialize: PartialFunction[(String, JObject), Any] = Map()
def serialize: PartialFunction[Any, JObject] = Map()
Эти методы могут быть переопределены при реализации TypeHints
признака (или при расширении предоставленной реализации по умолчанию TypeHints
), чтобы указать пользовательскую логику сериализации и десериализации для объектов JSON, у которых указаны подсказки типа. Реализации по умолчанию (показаны выше) — это просто фактически частичные функции, которые никогда ничему не соответствуют, поэтому они не имеют никаких эффектов.
Есть некоторые различия с методами deserialize
and serialize
в Serializer
, которые ранее использовались в нашем коде:
-
Эти методы не принимают
Formats
аргумент, что означает, что необходимо полагаться наFormats
экземпляр, который находится в области видимости. -
Они работают
JObject
на стороне преобразования в формате JSON, а не на его супертипеJValue
(очевидно, если подумать — потому что все, что имеет подсказку типа, неизбежно должно быть объектом JSON, а не строкой, числом или чем-то еще). -
Они не принимают параметр типа и просто работают
Any
на стороне Scala при преобразованиях — это потому, что они просто обрабатывают все типы с подсказками типа, которые требуют пользовательской логики сериализации в одной большой частичной функции. -
Вместо a
TypeInfo
deserialize
частичная функция принимает aString
, которое является значением поля подсказки типа.
Я думаю, что большинство этих различий связано с тем, что это более старый код lift-json, существовавший до появления Serializer
признака, и когда был только один способ выполнить пользовательскую сериализацию.
Итак, что сработало для меня, так это:
def typeHints(implicit formats: Formats) = new OurTypeHintsImpl(
… введите информацию о подсказках… ) {
override def deserialize = {
case ("type-hint-for-A", o: JObject) =>
… существующий код десериализации…
}
override def deserialize = {
case A(
… ) =>
… существующий код сериализации…
}
и чтобы добавить другой тип с подсказками типа и пользовательской логикой сериализации, было бы просто необходимо добавить одну новую case
ветвь к обоим из вышеперечисленных.
При таком подходе правильные подсказки типа добавляются автоматически с помощью lift-json, но вы все равно можете полностью настроить, как выполняется остальная часть сериализации и десериализации. Поэтому я думаю, что это наиболее удобный и подходящий подход для большинства случаев (но для этого потребовался небольшой рефакторинг). Также должна быть возможность переопределить подсказки типа в пользовательском Serializer
, но зачем изобретать велосипед?
Предупреждение: сопоставление регистров по типам по умолчанию имеет ограничения в отношении универсальных типов, но обычно это не должно иметь значения для этой цели — если только вы самостоятельно не сериализуете универсальный тип, содержащийся в другом типе, а вместо этого объединяете его во внешний тип в JSON.