Как добавить подсказки типа для класса с пользовательским сериализатором в lift-json?

#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 , которые ранее использовались в нашем коде:

  1. Эти методы не принимают Formats аргумент, что означает, что необходимо полагаться на Formats экземпляр, который находится в области видимости.

  2. Они работают JObject на стороне преобразования в формате JSON, а не на его супертипе JValue (очевидно, если подумать — потому что все, что имеет подсказку типа, неизбежно должно быть объектом JSON, а не строкой, числом или чем-то еще).

  3. Они не принимают параметр типа и просто работают Any на стороне Scala при преобразованиях — это потому, что они просто обрабатывают все типы с подсказками типа, которые требуют пользовательской логики сериализации в одной большой частичной функции.

  4. Вместо a TypeInfo deserialize частичная функция принимает a String , которое является значением поля подсказки типа.

Я думаю, что большинство этих различий связано с тем, что это более старый код 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.