使用 µPickle 序列化多态类型
Serializing polymorphic types with µPickle
我正在阅读 µPickle 的文档并在互联网上搜索,但我找不到任何关于一项非常基本的功能的提及,我记得我可能为我使用的所有序列化库都记录了它before (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 序列化多态类型?
µPickle 在编译时工作(宏在编译时工作)。为了派生具有 subclasses 实例的 trait 的类型 class 实例,你应该在编译时知道所有 trait subclasses。这仅适用于密封特征(通过 knownDirectSubclasses
https://github.com/lihaoyi/upickle/blob/master/implicits/src/upickle/implicits/internal/Macros.scala#L124 )。
http://www.lihaoyi.com/upickle/#SupportedTypes
Supported Types
Out of the box, uPickle supports writing and reading the following
types:
- Boolean, Byte, Char, Short, Int, Long, Float, Double
- Tuples from 1 to 22
- Immutable Seq, List, Vector, Set, SortedSet, Option, Array, Maps, and all other collections with a reasonable CanBuildFrom
implementation
- Duration, Either
- Stand-alone case classes and case objects, and their generic equivalents,
- Non-generic case classes and case objects that are part of a sealed trait or sealed class hierarchy
- sealed trait and sealed classes themselves, assuming that all subclasses are picklable
- UUIDs
- null
如您所见,仅支持密封特征。
解决方法是在多个源文件中具有密封特征,并使用自定义 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)
我正在阅读 µPickle 的文档并在互联网上搜索,但我找不到任何关于一项非常基本的功能的提及,我记得我可能为我使用的所有序列化库都记录了它before (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 序列化多态类型?
µPickle 在编译时工作(宏在编译时工作)。为了派生具有 subclasses 实例的 trait 的类型 class 实例,你应该在编译时知道所有 trait subclasses。这仅适用于密封特征(通过 knownDirectSubclasses
https://github.com/lihaoyi/upickle/blob/master/implicits/src/upickle/implicits/internal/Macros.scala#L124 )。
http://www.lihaoyi.com/upickle/#SupportedTypes
Supported Types
Out of the box, uPickle supports writing and reading the following types:
- Boolean, Byte, Char, Short, Int, Long, Float, Double
- Tuples from 1 to 22
- Immutable Seq, List, Vector, Set, SortedSet, Option, Array, Maps, and all other collections with a reasonable CanBuildFrom implementation
- Duration, Either
- Stand-alone case classes and case objects, and their generic equivalents,
- Non-generic case classes and case objects that are part of a sealed trait or sealed class hierarchy
- sealed trait and sealed classes themselves, assuming that all subclasses are picklable
- UUIDs
- null
如您所见,仅支持密封特征。
解决方法是在多个源文件中具有密封特征,并使用自定义 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)