Scala / Java中类似于Python的Pickle的简单,无障碍,零样板序列化?

在Scala / Java中有没有一种简单,轻松的序列化方法,类似于Python的pickle? Pickle是一个非常简单的解决scheme,它在空间和时间上相当高效(即不是糟糕的),但不关心跨语言的可访问性,版本控制等,并允许可选的定制。

我所知道的是:

  • Java的内置序列化非常慢( [1] , [2] ),臃肿和脆弱。 当有些东西可以清楚地序列化,但是没有注释的时候(比如很less的Point2D作者标记这些Serializable),也必须将类标记为Serializable。
  • Scala的BytePickle需要大量的样板文件来处理你想要的任何types,即使这样它也不能和(循环)对象图一起工作 。
  • jserial : 没有维护, 似乎没有那么快/比默认的Java序列化更小 。
  • kryo : 不能(取消)序列化没有0-参数的对象 ,这是一个严重的限制。 (你也必须注册你计划序列化的每一个课程,否则你会显着减速/膨胀 ,但即使如此,它仍然比腌菜更快。)
  • protostuff :AFAICT,你必须事先在“schema”中注册你想要序列化的每个类。

Kryo和protostuff是我find的最接近的解决scheme,但是我想知道是否还有其他东西(或者如果有一些方法可以使用,我应该知道)。 请包括使用示例! 理想情况下还包括基准。

斯卡拉现在有斯卡拉酸洗 ,根据情况performance好或比Kyro更好 – 请参阅幻灯片34-39。

我实际上认为你最好用克里奥(我不知道替代scheme提供更less的模式定义比非二进制协议)。 你提到腌菜不容易受到kryo在没有注册课程的情况下获得的减速和膨胀的影响,但即使没有注册课程,kryo仍然比腌菜更快,更不臃肿。 看到下面的微观基准(显然是用一粒盐,但这是我可以轻松做到的):

Python泡菜

 import pickle import time class Person: def __init__(self, name, age): self.name = name self.age = age people = [Person("Alex", 20), Person("Barbara", 25), Person("Charles", 30), Person("David", 35), Person("Emily", 40)] for i in xrange(10000): output = pickle.dumps(people, -1) if i == 0: print len(output) start_time = time.time() for i in xrange(10000): output = pickle.dumps(people, -1) print time.time() - start_time 

为我输出174字节和1.18-1.23秒(64位Linux上的Python 2.7.1)

斯卡拉kryo

 import com.esotericsoftware.kryo._ import java.io._ class Person(val name: String, val age: Int) object MyApp extends App { val people = Array(new Person("Alex", 20), new Person("Barbara", 25), new Person("Charles", 30), new Person("David", 35), new Person("Emily", 40)) val kryo = new Kryo kryo.setRegistrationOptional(true) val buffer = new ObjectBuffer(kryo) for (i <- 0 until 10000) { val output = new ByteArrayOutputStream buffer.writeObject(output, people) if (i == 0) println(output.size) } val startTime = System.nanoTime for (i <- 0 until 10000) { val output = new ByteArrayOutputStream buffer.writeObject(output, people) } println((System.nanoTime - startTime) / 1e9) } 

为我输出68字节和30-40ms(64位Linux上的Kryo 1.04,Scala 2.9.1,Java 1.6.0.26热点JVM)。 为了比较,如果我注册类,它会输出51字节和18-25毫秒。

对照

在注册课程时,Kryo使用大约40%的空间和3%的时间作为Python pickle,大约30%的空间和2%的注册时间。 你可以随时写一个自定义的序列化器,当你想要更多的控制。

Twitter的寒冷图书馆是真棒。 它使用Kryo进行序列化,但使用起来非常简单。 也很好:提供了一个MeatLocker [X]types,使任何X一个Serializable。

我会推荐SBinary 。 它使用implicits在编译时解决,所以它是非常有效的和typesafe。 它带有对许多常用Scala数据types的内置支持。 您必须手动编写您的(案例)类的序列化代码,但很容易做到。

一个简单的ADT的使用示例

另一个不错的select是最近的(2016) **netvl/picopickle**

  • 而几乎没有依赖性(核心库只依赖于无形 )。
  • 可扩展性 :你可以为你的types定义你自己的序列化器,你可以创build自定义的后端,也就是说,你可以使用不同的序列化格式(集合,JSON,BSON等)相同的库。 像空值处理这样的序列化行为的其他部分也可以定制。
  • 灵活性和便利性:默认的序列化格式对于大多数用途来说是很好的,但是可以通过方便的转换器DSL的支持几乎任意地进行定制。
  • 无reflection的静态序列化:无形状genericsmacros用于为任意types提供序列化器,这意味着不使用reflection。

例如:

基于Jawn的pickler还提供了额外的函数readString() / writeString()readAst() / writeAst() ,它们分别将对象序列化为string,将JSON AST序列化为string:

 import io.github.netvl.picopickle.backends.jawn.JsonPickler._ case class A(x: Int, y: String) writeString(A(10, "hi")) shouldEqual """{"x":10,"y":"hi"}""" readString[A]("""{"x":10,"y":"hi"}""") shouldEqual A(10, "hi")