即使 Scala 中的简单序列化示例也不起作用。为什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23182577/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Even trivial serialization examples in Scala don't work. Why?
提问by MKaama
I am trying the simplest possible serialization examples of a class:
我正在尝试最简单的类序列化示例:
@serializable class Person(age:Int) {}
val fred = new Person(45)
import java.io._
val out = new ObjectOutputStream(new FileOutputStream("test.obj"))
out.writeObject(fred)
out.close()
This throws exception "java.io.NotSerializableException: Main$$anon$1$Person" on me. Why? Is there a simple serialization example? I also tried
这会在我身上引发异常“java.io.NotSerializableException: Main$$anon$1$Person”。为什么?有没有简单的序列化例子?我也试过
@serializable class Person(nm:String) {
private val name:String=nm
}
val fred = new Person("Fred")
...
and tried to remove @serializableand some other permutations. The file "test.obj" is created, over 2Kb in size and has plausible contents.
并试图删除@serializable和其他一些排列。创建了文件“test.obj”,大小超过 2Kb,内容可信。
EDIT:
编辑:
Reading the "test.obj" back in (from the 2nd answer below) causes
重新读取“test.obj”(来自下面的第二个答案)导致
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_51). Type in expressions to have them evaluated. Type :help for more information.
scala> import java.io._ import java.io._
scala> val fis = new FileInputStream( "test.obj" ) fis: java.io.FileInputStream = java.io.FileInputStream@716ad1b3
scala> val oin = new ObjectInputStream( fis ) oin: java.io.ObjectInputStream = java.io.ObjectInputStream@1f927f0a
scala> val p= oin.readObject java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: Main$$anon$1 at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354) at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) at .(:12) at .() at .(:7) at .() at $print() at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734) at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983) at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573) at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604) at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568) at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:756) at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801) at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713) at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:577) at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584) at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587) at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:878) at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833) at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833) at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135) at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833) at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83) at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96) at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105) at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) Caused by: java.io.NotSerializableException: Main$$anon$1 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) at Main$$anon$1.(a.scala:11) at Main$.main(a.scala:1) at Main.main(a.scala) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:71) at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31) at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:139) at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:71) at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:139) at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:28) at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45) at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:35) at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45) at scala.tools.nsc.ScriptRunner.scala$tools$nsc$ScriptRunner$$runCompiled(ScriptRunner.scala:171) at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:188) at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:188) at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply$mcZ$sp(ScriptRunner.scala:157) at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:131) at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:131) at scala.tools.nsc.util.package$.trackingThreads(package.scala:51) at scala.tools.nsc.util.package$.waitingForThreads(package.scala:35) at scala.tools.nsc.ScriptRunner.withCompiledScript(ScriptRunner.scala:130) at scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:188) at scala.tools.nsc.ScriptRunner.runScriptAndCatch(ScriptRunner.scala:201) at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:76) ... 3 more
欢迎使用 Scala 版本 2.10.3(Java HotSpot(TM) 64 位服务器 VM,Java 1.7.0_51)。输入表达式以对它们进行评估。输入 :help 以获取更多信息。
Scala> 导入 java.io._ 导入 java.io._
scala> val fis = new FileInputStream("test.obj") fis: java.io.FileInputStream = java.io.FileInputStream@716ad1b3
scala> val oin = new ObjectInputStream( fis ) oin: java.io.ObjectInputStream = java.io.ObjectInputStream@1f927f0a
scala> val p= oin.readObject java.io.WriteAbortedException:写入中止;java.io.NotSerializableException: Main$$anon$1 at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354) at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990) at java.io.ObjectInputStream.readSerialData( ObjectInputStream.java:1915) 在 java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) 在 java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) 在 java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) ) at .(:12) at .() at .(:7) at .() at $print() at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl。
回答by Vladimir Matveev
Note that @serializablescaladoc tells that it is deprecated since 2.9.0:
请注意,@serializablescaladoc 表明它自 2.9.0 起已被弃用:
Deprecated (Since version 2.9.0) instead of @serializable class C, use class C extends Serializable
不推荐使用(自版本 2.9.0)代替 @serializable class C,使用 class C extends Serializable
So you just have to use Serializabletrait:
所以你只需要使用Serializabletrait:
class Person(val age: Int) extends Serializable
This works for me (type :pastein REPL and paste these lines):
这对我:paste有用(输入REPL 并粘贴这些行):
import java.io.{ObjectOutputStream, ObjectInputStream}
import java.io.{FileOutputStream, FileInputStream}
class Person(val age: Int) extends Serializable {
override def toString = s"Person($age)"
}
val os = new ObjectOutputStream(new FileOutputStream("/tmp/example.dat"))
os.writeObject(new Person(22))
os.close()
val is = new ObjectInputStream(new FileInputStream("/tmp/example.dat"))
val obj = is.readObject()
is.close()
obj
This is the output:
这是输出:
// Exiting paste mode, now interpreting.
import java.io.{ObjectOutputStream, ObjectInputStream}
import java.io.{FileOutputStream, FileInputStream}
defined class Person
os: java.io.ObjectOutputStream = java.io.ObjectOutputStream@5126abfd
is: java.io.ObjectInputStream = java.io.ObjectInputStream@41e598aa
obj: Object = Person(22)
res8: Object = Person(22)
So, you can see, the [de]serialization attempt was successful.
因此,您可以看到,[反] 序列化尝试成功了。
Edit (on why you're getting NotSerializableExceptionwhen you run Scala script from file)
编辑(关于NotSerializableException从文件运行 Scala 脚本时为什么会得到)
I've put my code into a file and tried to run it via scala test.scalaand got exactly the same error as you. Here is my speculation on why it happens.
我已将我的代码放入一个文件中并尝试通过它运行它scala test.scala并得到与您完全相同的错误。这是我对它为什么会发生的猜测。
According to the stack trace a weird class Main$$anon$1is not serializable. Logical question is: why it is there in the first place? We're trying to serialize Personafter all, not something weird.
根据堆栈跟踪,一个奇怪的类Main$$anon$1是不可序列化的。逻辑问题是:为什么它首先存在?Person毕竟我们正在尝试序列化,而不是奇怪的东西。
Scala script is special in that it is implicitly wrapped into an object called Main. This is indicated by the stack trace:
Scala 脚本的特殊之处在于它被隐式包装到一个名为Main. 这由堆栈跟踪指示:
at Main$$anon.<init>(test.scala:9)
at Main$.main(test.scala:1)
at Main.main(test.scala)
The names here suggest that Main.mainstatic method is the program entry point, and this method delegates to Main$.maininstance method (object's class is named after the object but with $appended). This instance method in turn tries to create an instance of a class Main$$anon$1. As far as I remember, anonymous classes are named that way.
这里的名称表明Main.main静态方法是程序入口点,并且该方法委托给Main$.main实例方法(object的类以对象命名但$附加)。这个实例方法反过来尝试创建一个类的实例Main$$anon$1。据我所知,匿名类就是这样命名的。
Now, let's try to find exact Personclass name (run this as Scala script):
现在,让我们尝试找到确切的Person类名(将其作为 Scala 脚本运行):
class Person(val age: Int) extends Serializable {
override def toString = s"Person($age)"
}
println(new Person(22).getClass)
This prints something I was expecting:
这打印了我期待的东西:
class Main$$anon$Person
This means that Personis not a top-level class; instead it is a nested class defined in the anonymous class generated by the compiler! So in fact we have something like this:
这意味着这Person不是顶级课程;相反,它是在编译器生成的匿名类中定义的嵌套类!所以实际上我们有这样的事情:
object Main {
def main(args: Array[String]) {
new { // this is where Main$$anon is generated, and the following code is its constructor body
class Person(val age: Int) extends Serializable { ... }
// all other definitions
}
}
}
But in Scala all nested classes are something called "nested non-static" (or "inner") classes in Java. This means that these classes always contain an implicit referenceto an instance of their enclosing class. In this case, enclosing class is Main$$anon$1. Because of that when Java serializer tries to serialize Person, it transitively encounters an instance of Main$$anon$1and tries to serialize it, but since it is not Serializable, the process fails. BTW, serializing non-static inner classes is a notorious thing in Java world, it is known to cause problems like this one.
但是在 Scala 中,所有嵌套类在 Java 中都是称为“嵌套非静态”(或“内部”)类的东西。这意味着这些类始终包含对其封闭类的实例的隐式引用。在这种情况下,封闭类是Main$$anon$1. 因此,当 Java 序列化程序尝试序列化时Person,它会传递地遇到 的实例Main$$anon$1并尝试对其进行序列化,但由于不是Serializable,因此该过程失败。顺便说一句,序列化非静态内部类在 Java 世界中是一件臭名昭著的事情,众所周知会导致这样的问题。
As for why it works in REPL, it seems that in REPL declared classes somehow do not end up as inner ones, so they don't have any implicit fields. Hence serialization works normally for them.
至于为什么它在 REPL 中有效,似乎在 REPL 中声明的类以某种方式最终不会成为内部类,因此它们没有任何隐式字段。因此,序列化通常对它们有效。
回答by Andreas Neumann
You could use the Serializable Trait:
您可以使用 Serializable Trait:
Trivial Serialization example using Java Serialization with the Serializable Trait:
使用带有 Serializable Trait 的 Java 序列化的简单序列化示例:
case class Person(age: Int) extends Serializable
Usage:
用法:
Serialization, Write Object
序列化,写对象
val fos = new FileOutputStream( "person.serializedObject" )
val o = new ObjectOutputStream( fos )
o writeObject Person(31)
Deserialization, Read Object
反序列化,读取对象
val fis = new FileInputStream( "person.serializedObject" )
val oin = new ObjectInputStream( fis )
val p= oin.readObject
Which creates following output
这会创建以下输出
fis: java.io.FileInputStream = java.io.FileInputStream@43a2bc95
oin: java.io.ObjectInputStream = java.io.ObjectInputStream@710afce3
p: Object = Person(31)
As you see the deserialization can't infer the Object Type itself, which is a clear drawback.
如您所见,反序列化无法推断对象类型本身,这是一个明显的缺点。
Serialization with Scala-Pickling
使用 Scala-Pickle 进行序列化
https://github.com/scala/picklingor part of the Standard-Distribution starting with Scala 2.11
https://github.com/scala/pickling或从 Scala 2.11 开始的 Standard-Distribution 的一部分
In the exmple code the object is not written to a file and JSON is used instead of ByteCode Serialization which avoids certain problems originating in byte code incompatibilities between different Scala version.
在示例代码中,对象没有写入文件,而是使用 JSON 代替字节码序列化,这避免了由于不同 Scala 版本之间的字节码不兼容而引起的某些问题。
import scala.pickling._
import json._
case class Person(age: Int)
val person = Person(31)
val pickledPerson = person.pickle
val unpickledPerson = pickledPerson.unpickle[Person]
回答by My other car is a cadr
class Person(age:Int) {}is equivalent to the Java code:
class Person(age:Int) {}相当于Java代码:
class Person{
Person(Int age){}
}
which is probably not what you want. Note that the parameter ageis simply discarded and Personhas no member fields.
这可能不是您想要的。请注意,该参数age被简单地丢弃并且Person没有成员字段。
You want either:
你想要:
@serializable case class Person(age:Int)@serializable class Person(val age:Int)
@serializable case class Person(age:Int)@serializable class Person(val age:Int)
You can leave out the empty curly brackets at the end. In fact, it's encouraged.
您可以省略末尾的空大括号。事实上,这是鼓励的。

![scala 将 List[Any] 转换为 List[Int]](/res/img/loading.gif)