Объединение типов и полевых сериализаторов

Предположим, что у меня есть класс case со следующей установкой:

case class Place(id:java.util.UUID, name:String)

Я могу написать (рабочий!) сериализатор для этого типа следующим образом:

class placeSerializer extends CustomSerializer[Place]( format => (
        {
            case JObject(JField("id", JString(s)) :: JField("name",JString(x)) :: Nil ) =>
                Place(UUID.fromString(s), x)
        },
        {
            case x:Place =>
                JObject(
                  JField("id", JString(x.id.toString())) :: 
                  JField("name", JString(x.name)) :: Nil)
        }
        )
    )

Но если у моего класса case в конечном итоге будет намного больше полей, это может привести к тому, что я перечислил всю структуру объекта с помощью AST, создав что-то очень подробное только для кодирования примитивов.

json4s, похоже, имеет полевые сериализаторы, которые могут действовать только в определенных полях, при этом включаются методы шаблонов, которые легко преобразуют имена и отбрасывают поля. Тем не менее, они имеют следующую подпись для своих serialize и deserialize частичных функций:

case class FieldSerializer[A: Manifest](
  serializer:   PartialFunction[(String, Any), Option[(String, Any)]] = Map(),
  deserializer: PartialFunction[JField, JField] = Map()
)

Так как JField (тип, представляющий ключ → val от json), является его собственным типом, а не подклассом JValue, как я могу объединить эти два типа сериализаторов для правильного кодирования id по его имени, до UUID, сохраняя при этом обработку по умолчанию других полей (которые являются примитивными типами данных).

По сути, я хотел бы, чтобы цепочка формата, которая понимает поле внутри Place, является UUID, без необходимости указывать структуру AST для всех полей, которые уже могут обрабатывать DefaultFormats.

То, что я ищу специально, - это подражать шаблону, аналогичному JSONEncoder и JSONDecoder интерфейсам в python, которые могут использовать имя ключа как а также тип значения, чтобы определить, как обрабатывать маршаллинг для поля.

Ответ 1

Хитрость заключается в том, чтобы не писать сериализатор для вашего типа, а для типа, который вы используете внутри (в данном случае java.util.UUID)

Затем вы можете добавить этот сериализатор в панель инструментов, а затем любой тип с использованием UUID будет работать точно так же, как и типы, поддерживаемые полями DefaultSerializer:

case object UUIDSerialiser extends CustomSerializer[UUID](format => (
    {
      case JString(s) => UUID.fromString(s)
      case JNull => null
    },
    {
      case x: UUID => JString(x.toString)
    }
  )
)

implicit val json4sFormats = Serialization.formats(NoTypeHints) + UUIDSerialiser

Обновить ссылку на PR

Обновление 2 PR был объединен, и теперь, в случае UUID, вы можете использовать:

import org.json4s.ext.JavaTypesSerializers

implicit val json4sFormats = Serialization.formats(NoTypeHints) ++ JavaTypesSerializers.all

Ответ 2

Теперь в пакете extras json4s имеется сериализатор UUID. Скорее всего, он будет доступен в версии 3.2.11 (который не был выпущен на момент написания этой статьи).

Вы сможете сделать что-то вроде этого:

import org.json4s.ext.JavaTypesSerializers

implicit val json4sFormats = Serialization.formats(NoTypeHints) ++ JavaTypesSerializers.all

Это было сделано из тестов для этой функции PR.