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