【问题标题】:Scala pickling: how?Scala酸洗:如何?
【发布时间】:2014-05-29 02:56:50
【问题描述】:

我正在尝试使用“酸洗”序列化是 Scala,我看到相同的示例演示它:

import scala.pickling._
import json._

val pckl = List(1, 2, 3, 4).pickle

解酸就像酸洗一样简单:

val lst = pckl.unpickle[List[Int]]

这个例子提出了一些问题。首先,它跳过了对象到字符串的转换。显然你需要调用 pckl.value 来获取 json 字符串表示。

Unpickling 更加令人困惑。反序列化是将字符串(或字节)转换为对象的行为。如果没有对象的字符串/二进制表示,这个“示例”如何演示反序列化?

那么,如何使用酸洗库反序列化简单对象?

【问题讨论】:

  • 你签出the tests了吗?看起来还有更多的例子here。
  • 是的,我检查了这些测试,但它们表现出完全相同的行为:它们解开的不是字符串或字节数组,而是解开一个泡菜。那么当我所拥有的只有 json 字符串和对象类型时,pickle 是从哪里来的呢?

标签: scala serialization scala-pickling


【解决方案1】:

使用类型系统和案例类来实现您的目标。您可以取消选择层次结构中的某些高级类型(直到并包括 AnyRef)。这是一个例子:

trait Zero
case class One(a:Int) extends Zero
case class Two(s:String) extends Zero

object Test extends App {
  import scala.pickling._
  import json._

  // String that can be sent down a wire
  val wire: String = Two("abc").pickle.value

  // On the other side, just use a case class
  wire.unpickle[Zero] match {
    case One(a) => println(a)
    case Two(s) => println(s)
    case unknown => println(unknown.getClass.getCanonicalName)
  }
}

【讨论】:

  • 谢谢大卫,这是一个很好的例子,它展示了我所问的(对象->字符串->对象)以及我没有问的内容(类型未知时如何处理大小写:)跨度>
  • 谢谢!我现在正在使用大约 30 个不同类别的酸洗。与 Kryo 和 Jackson 相比,它真的是零样板。将 unpickle 包裹在 Try() 中以获得更多好处。
【解决方案2】:

好的,我想我明白了。

import scala.pickling._
import json._

var str = Array(1,2,3).pickle.value // this is JSON string
println(str)
val x = str.unpickle[Array[Int]]    // unpickle from string

将产生 JSON 字符串:

{
  "tpe": "scala.Array[scala.Int]",
  "value": [
    1,
    2,
    3
  ]
}

所以,就像我们腌制任何类型一样,我们可以取消腌制字符串。序列化类型由“json.”中声明的隐式格式化程序规定,可以用“binary.”代替

【讨论】:

    【解决方案3】:

    看起来你将从一个泡菜开始解泡到一个案例类。但是可以将 JSON 字符串提供给 JSONPickle 类以获取起始泡菜。

    这是一个基于their array-json test的示例

    package so
    
    import scala.pickling._
    import json._
    
    case class C(arr: Array[Int]) { override def toString = s"""C(${arr.mkString("[", ",", "]")})""" }
    
    object PickleTester extends App {
    
      val json = """{"arr":[ 1, 2, 3 ]}"""
      val cPickle = JSONPickle( json )
      val unpickledC: C = cPickle.unpickle[C]
    
      println( s"$unpickledC, arr.sum = ${unpickledC.arr.sum}" )
    }
    

    打印的输出是:

    C([1,2,3]), arr.sum = 6
    

    我能够从测试中删除“tpe”以及来自测试的输入 JSON 上的 .stripMargin.trim。它在一行中起作用,但我认为它可能会更明显地分开。我不清楚测试中的“tpe”是否应该为传入的 JSON 提供类型安全度量。

    看起来他们唯一支持酸洗的其他类是 BinaryPickle 除非你想自己动手。最新的 scala-pickling 快照 jar requires quasiquotes 用于编译此答案中的代码。


    我今天早上尝试了一些更复杂的东西,发现传入的 JSON 中的非基元需要“tpe” - 这表明序列化的字符串确实必须与 pickler 兼容(我将其混合到上面的代码中):

    case class J(a: Option[Boolean], b: Option[String], c: Option[Int]) { override def toString = s"J($a, $b, $c)" }
    
    ...
    val jJson = """{"a": {"tpe": "scala.None.type"},
    | "b":{"tpe": "scala.Some[java.lang.String]","x":"donut"},
    | "c":{"tpe": "scala.Some[scala.Int]","x":47}}"""
    val jPickle = JSONPickle( jJson.stripMargin.trim )
    val unpickledJ: J = jPickle.unpickle[J]
    
    println( s"$unpickledJ" )
    ...
    

    当然,我必须在J(None, Some("donut"), Some(47)) 上使用.value 来确定如何创建jJson 输入值,以防止unpickling 引发异常。

    J 的输出如下:

    J(None, Some(donut), Some(47)) 
    

    查看this test,似乎如果传入的 JSON 是 JSONPickle 魔法起作用的所有基元或案例类(或组合),但其他一些类(如 Options)需要额外的“tpe”类型信息才能正确解压。

    【讨论】:

    • 我的问题是第 23 行:github.com/scala/pickling/blob/2.10.x/core/src/test/scala/… 它使用数组 [1..3] 创建 pickle,然后使用数组 [1..3] 对 JSON 执行 unpickle。所以我不明白,正在测试什么。我怎么知道这个测试的结果来自 JSON 而不是 pickle 的显式构造函数?这个测试假设我们知道反序列化的结果是什么,但重点是什么?
    • 你说得对,整个过程需要了解泡菜的两端。我认为他们的观点是快速、类型安全的序列化/反序列化,他们选择避免​​运行时反射并改用编译时代码扩展。在我的第一个示例中,从 JSON 字符串加载带有原语的案例类似乎是 JSONPickle 类允许的特殊情况。然而,它不是一个通用的 JSON 解析器,因为它在处理 Options 时需要帮助,并且似乎不会为缺少的 JSON 成员应用默认值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-27
    • 1970-01-01
    相关资源
    最近更新 更多