【问题标题】:Scala Pickling: Writing a custom pickler / unpickler for nested structuresScala Pickling:为嵌套结构编写自定义pickler / unpickler
【发布时间】:2013-10-07 05:36:30
【问题描述】:

我正在尝试编写一个自定义的 SPickler / Unpickler 对来解决当前 scala-pickling 的一些限制。 我试图腌制的数据类型是一个案例类,其中一些字段已经有自己的 SPickler 和 Unpickler 实例。 我想在我的自定义pickler 中使用这些实例,但我不知道如何。

这是我的意思的一个例子:

// 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)
  }
}

我非常感谢一个工作示例。 谢谢!

【问题讨论】:

    标签: scala scala-pickling


    【解决方案1】:

    这是一个工作示例:

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

    注意MyClass 的类型参数不需要上下文边界。只有自定义pickler类需要相应的隐式:

    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)
      }
    
    }
    

    除了自定义pickler类,我们还需要一个隐式def,它返回一个专门用于具体类型参数的pickler实例:

    implicit def myClassPickler[A: SPickler: Unpickler: FastTypeTag](implicit pf: PickleFormat) =
      new MyClassPickler
    

    【讨论】:

    • 这行得通,但有一个奇怪的不对称性:在pickle 中,字段被命名为builder.putField("myString", ...),但我们在unpickle 中不使用这些名称。这些名称是可选的吗?如果是,我们如何避免使用它们?
    • 一些像 JSON 这样的 pickle 格式将字段名称放入 pickle。即使它们有时未被使用,目前也无法避免使用它们。不过,API 仍在略微发展。
    猜你喜欢
    • 2020-11-21
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 2018-09-12
    • 2019-08-28
    • 1970-01-01
    • 2018-06-07
    • 2021-05-31
    相关资源
    最近更新 更多