用µPickle序列化多态类型

时间:2019-04-30 08:53:33

标签: scala serialization upickle

我正在阅读µPickle的文档并在互联网上搜索,但是我找不到任何提到的非常基本的功能,因此我记得以前我使用过的所有序列化库都记录了该功能(杰克逊(Jackson,Prickle)...):多态类型。我发现的唯一文档是密封的特征/类。考虑以下代码:

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 个答案:

答案 0 :(得分:3)

µPickle在编译时工作(宏在编译时工作)。为了派生具有子类实例的trait的类型类实例,您应该在编译时知道所有trait子类。这仅适用于密封特征(通过knownDirectSubclasses https://github.com/lihaoyi/upickle/blob/master/implicits/src/upickle/implicits/internal/Macros.scala#L124)。

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

  

支持的类型

     

开箱即用,uPickle支持编写和阅读以下内容   类型:

     
      
  • 布尔型,字节,字符,短型,整数,长型,浮点型,双精度型
  •   
  • 从1到22的组合
  •   
  • 不可变的Seq,List,Vector,Set,SortedSet,Option,Array,Maps和所有其他具有合理的CanBuildFrom的集合   实施
  •   
  • 持续时间,要么
  •   
  • 独立案例类和案例对象,以及它们的一般等效项,
  •   
  • 属于密封特征或密封类层次结构的非通用案例类和案例对象
  •   
  • 密封特征和密封类本身,假设所有子类都是可腌制的
  •   
  • UUID
  •   
  •   

如您所见,仅支持密封特征。


解决方法是在多个源文件中使用特征密封,并使用自定义Pickler对通用父特征进行密封。

  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)