Сериализация полиморфных типов с помощью µPickle - PullRequest
2 голосов
/ 30 апреля 2019

Я читаю документацию для µPickle и ищу в Интернете, но я не смог найти упоминаний об одной функции, которая является довольно простой, и я помню, как она была документирована, возможно, для всех библиотек сериализации, которые я использовалдо (Джексон, Колючка ...): полиморфные типы.Единственная документация, которую я нашел, касается запечатанных черт / классов.Рассмотрим следующий код:

import upickle.default._

trait Base

object Base{
  implicit val rw: ReadWriter[Base] = ReadWriter.merge(C1.rw, C2.rw)
}
object C1 {
  implicit val rw: ReadWriter[C1] = macroRW
}
object C2 {
  implicit val rw: ReadWriter[C2] = macroRW
}
case class C1(x: Int) extends Base
case class C2(s: String) extends Base

object Main extends App {
  val c1: Base = new C1(0)
  val c2: Base = new C2("X")

  val c1String = write(c1)
  val c2String = write(c2)
  println("c1 " + c1String)
  println("c2 " + c2String)

}

Этот код работал бы, если бы я изменил trait Base на sealed trait Base.Я согласен с требованием перечислить все производные классы в сериализаторе, это то же самое, что и в других упомянутых мной библиотеках, но не всегда возможно или желательно иметь несколько больших классов в одном исходном файле, чтобы база моглабыть запечатанным.Как можно сериализовать полиморфные типы с помощью uPickle, если база не запечатана?

1 Ответ

3 голосов
/ 30 апреля 2019

µPickle работает во время компиляции (макросы работают во время компиляции). Чтобы получить экземпляр класса типа для признака, имеющего экземпляры для подклассов, вы должны знать все подклассы признака во время компиляции. Это возможно только для запечатанной черты (через knownDirectSubclasses https://github.com/lihaoyi/upickle/blob/master/implicits/src/upickle/implicits/internal/Macros.scala#L124).

http://www.lihaoyi.com/upickle/#SupportedTypes

Поддерживаемые типы

Из коробки uPickle поддерживает запись и чтение следующего Типы:

  • Boolean, Byte, Char, Short, Int, Long, Float, Double
  • Кортежи от 1 до 22 * ​​1019 *
  • Неизменяемый Seq, List, Vector, Set, SortedSet, Option, Array, Maps и все другие коллекции с разумным CanBuildFrom осуществление
  • Продолжительность, либо
  • Автономные классы case и объекты case и их общие эквиваленты,
  • Неуниверсальные классы прецедентов и объекты прецедентов, которые являются частью иерархии запечатанных признаков или замкнутых классов
  • Запечатанные черты и сами запечатанные классы, при условии, что все подклассы могут быть выбраны
  • UUID,
  • 1033 * пустой *

Как видите, поддерживаются только запечатанные черты.


Обходной путь должен иметь запечатанные черты в нескольких исходных файлах и общую родительскую черту с пользовательским средством выбора.

  trait Base

  object Base {
    implicit val rw: ReadWriter[Base] = readwriter[ujson.Value].bimap[Base]({
      case c: Base1 => writeJs(c)
      case c: Base2 => writeJs(c)
    },
      s => Try(read[Base1](s)).getOrElse(read[Base2](s))
    )
  }

  sealed trait Base1 extends Base
  object Base1 {
    implicit val rw: ReadWriter[Base1] = ReadWriter.merge(C1.rw, C11.rw)
  }

  case class C1(x: Int) extends Base1
  object C1 {
    implicit val rw: ReadWriter[C1] = macroRW
  }

  case class C11(x: Int) extends Base1
  object C11 {
    implicit val rw: ReadWriter[C11] = macroRW
  }

  sealed trait Base2 extends Base
  object Base2 {
    implicit val rw: ReadWriter[Base2] = ReadWriter.merge(C2.rw, C22.rw)
  }

  case class C2(s: String) extends Base2
  object C2 {
    implicit val rw: ReadWriter[C2] = macroRW
  }

  case class C22(s: String) extends Base2
  object C22 {
    implicit val rw: ReadWriter[C22] = macroRW
  }

  val c1: Base = new C1(0)
  val c2: Base = new C2("X")

  val c1String = write(c1)
  val c2String = write(c2)
  println("c1 " + c1String) // c1 {"$type":"App.C1","x":0}
  println("c2 " + c2String) // c2 {"$type":"App.C2","s":"X"}

  println(read[Base](c1String)) // C1(0)
  println(read[Base](c2String)) // C2(X)
...