Scala Pickling: Написание собственного средства выбора / удаления для вложенных структур

Я пытаюсь написать пользовательскую пару SPickler / Unpickler, чтобы обойти некоторые текущие ограничения скалолазания. Тип данных, который я пытаюсь выбрать, - это класс case, в котором некоторые поля уже имеют свои собственные экземпляры SPickler и Unpickler. Я хотел бы использовать эти экземпляры в моем сборщике, но я не знаю как.

Вот пример того, что я имею в виду:

// Here's a class for which I want a custom SPickler / Unpickler.
// One of its fields can already be pickled, so I'd like to reuse that logic.
case class MyClass[A: SPickler: Unpickler: FastTypeTag](myString: String, a: A)

// Here's my custom pickler.
class MyClassPickler[A: SPickler: Unpickler: FastTypeTag](
  implicit val format: PickleFormat) extends SPickler[MyClass[A]] with Unpickler[MyClass[A]] {
  override def pickle(
    picklee: MyClass[A],
    builder: PBuilder) {
    builder.beginEntry(picklee)

    // Here we save `myString` in some custom way.
    builder.putField(
      "mySpecialPickler",
      b => b.hintTag(FastTypeTag.ScalaString).beginEntry(
        picklee.myString).endEntry())

    // Now we need to save `a`, which has an implicit SPickler.
    // But how do we use it?

    builder.endEntry()
  }

  override def unpickle(
    tag: => FastTypeTag[_],
    reader: PReader): MyClass[A] = {
    reader.beginEntry()

    // First we read the string.
    val myString = reader.readField("mySpecialPickler").unpickle[String]

    // Now we need to read `a`, which has an implicit Unpickler.
    // But how do we use it?
    val a: A = ???

    reader.endEntry()

    MyClass(myString, a)
  }
}

Я был бы очень признателен за рабочий пример. Спасибо!

1 ответ

Решение

Вот рабочий пример:

case class MyClass[A](myString: String, a: A)

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

class MyClassPickler[A](implicit val format: PickleFormat, aTypeTag: FastTypeTag[A],
                                     aPickler: SPickler[A], aUnpickler: Unpickler[A])
  extends SPickler[MyClass[A]] with Unpickler[MyClass[A]] {

  private val stringUnpickler = implicitly[Unpickler[String]]

  override def pickle(picklee: MyClass[A], builder: PBuilder) = {
    builder.beginEntry(picklee)

    builder.putField("myString",
      b => b.hintTag(FastTypeTag.ScalaString).beginEntry(picklee.myString).endEntry()
    )

    builder.putField("a",
      b => {      
        b.hintTag(aTypeTag)
        aPickler.pickle(picklee.a, b)
      }
    )

    builder.endEntry()
  }

  override def unpickle(tag: => FastTypeTag[_], reader: PReader): MyClass[A] = {
    reader.hintTag(FastTypeTag.ScalaString)
    val tag = reader.beginEntry()
    val myStringUnpickled = stringUnpickler.unpickle(tag, reader).asInstanceOf[String]
    reader.endEntry()

    reader.hintTag(aTypeTag)
    val aTag = reader.beginEntry()
    val aUnpickled = aUnpickler.unpickle(aTag, reader).asInstanceOf[A]
    reader.endEntry()

    MyClass(myStringUnpickled, aUnpickled)
  }

}

В дополнение к пользовательскому классу выбора, нам также нужно неявное определение, которое возвращает экземпляр выбора, специализированный для конкретных аргументов типа:

implicit def myClassPickler[A: SPickler: Unpickler: FastTypeTag](implicit pf: PickleFormat) =
  new MyClassPickler
Другие вопросы по тегам