9

I'm trying to use "pickling" serialization is Scala, and I see the same example demonstrating it:

import scala.pickling._
import json._

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

Unpickling is just as easy as pickling:

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

This example raises some question. First of all, it skips converting of object to string. Apparently you need to call pckl.value to get json string representation.

Unpickling is even more confusing. Deserialization is an act of turning string (or bytes) into an object. How come this "example" demonstrates deserialization if there is no string/binry representation of object?

So, how do I deserialize simple object with pickling library?

Alt
  • 2,597
  • 5
  • 26
  • 36
Vadym Chekan
  • 4,977
  • 2
  • 31
  • 24
  • Have you checked out [the tests](https://github.com/scala/pickling/tree/2.10.x/core/src/test/scala/pickling)? Looks like there are some more examples [here](https://speakerdeck.com/heathermiller/on-pickles-and-spores-improving-support-for-distributed-programming-in-scala) too. – n0741337 Apr 15 '14 at 00:59
  • Yes, I checked those tests, but they exhibit exactly the same behavior: they unpickle not string or byte array but unpickle a pickle. So where pickle is coming from in the real-world situation when all I have is json string and object type? – Vadym Chekan Apr 15 '14 at 06:17

3 Answers3

6

Use the type system and case classes to achieve your goals. You can unpickle to some superior type in your hierarchy (up to and including AnyRef). Here is an example:

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)
  }
}
David Weber
  • 1,965
  • 1
  • 22
  • 32
  • Thanks David, that's a good example which demonstrates what I've asked (object->string->object) plus what I did not ask (how to handle case when type is unknown :) – Vadym Chekan Jun 02 '14 at 18:39
  • 1
    Thanks! I am now using pickling with about 30 different classes. It is really zero boilerplate compared to Kryo and Jackson. Wrap the unpickle in a Try() for more goodness. – David Weber Jun 04 '14 at 08:57
3

Ok, I think I understood it.

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

will produce JSON string:

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

So, the same way we pickle any type, we can unpickle string. Type of serialization is regulated by implicit formatter declared in "json." and can be replaced by "binary."

Vadym Chekan
  • 4,977
  • 2
  • 31
  • 24
0

It does look like you will be starting with a pickle to unpickle to a case class. But the JSON string can be fed to the JSONPickle class to get the starting pickle.

Here's an example based on 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}" )
}

The output printed is:

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

I was able to drop the "tpe" in from the test as well as the .stripMargin.trim on the input JSON from the test. It works all in one line, but I thought it might be more apparent split up. It's unclear to me if that "tpe" from the test is supposed to provide a measure of type safety for the incoming JSON.

Looks like the only other class they support for pickling is a BinaryPickle unless you want to roll your own. The latest scala-pickling snapshot jar requires quasiquotes to compile the code in this answer.


I tried someting more complicated this morning and discovered that the "tpe" is required for non-primatives in the incoming JSON - which points out that the serialized string really must be compatible with the pickler( which I mixed into the above code ):

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" )
...

where naturually, I had to use .value on a J(None, Some("donut"), Some(47)) to figure out how to create the jJson input value to prevent the unpickling from throwing an exception.

The output for J is like:

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

Looking at this test, it appears that if the incoming JSON is all primatives or case classes (or combinations) that the JSONPickle magic works, but some other classes like Options require extra "tpe" type information to unpickle correctly.

n0741337
  • 2,474
  • 2
  • 15
  • 15
  • My problem is line #23: https://github.com/scala/pickling/blob/2.10.x/core/src/test/scala/pickling/array-json.scala#L23 It creates pickle with array [1..3], and than performs unpickle on JSON with array [1..3]. So I do not understand, what is being tested. How do I know that result of this test is coming from JSON and not from the explicit constructor of pickle? This test assumes we know what is the result of deserialization but what is the point than? – Vadym Chekan Apr 15 '14 at 17:45
  • You're correct that the whole process requires knowledge of both ends of the pickle. I think their point is fast, typesafe serializatin/deserialization where they made choices to avoid runtime reflection and use compile time code expansion instead. Loading a case class with primitives from a JSON string like in my first example appears to be a special case allowed by the `JSONPickle` class. It is not, however, a generic JSON parser in the sense that it needs help during unpickling when dealing Options and doesn't appear to apply default values from for missing JSON members. – n0741337 Apr 15 '14 at 18:50