简体   繁体   English

即使Scala中的琐碎序列化示例也不起作用。 为什么?

[英]Even trivial serialization examples in Scala don't work. Why?

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. 这会抛出异常“java.io.NotSerializableException:Main $$ anon $ 1 $ Person”。 Why? 为什么? Is there a simple serialization example? 有简单的序列化示例吗? I also tried 我也试过了

@serializable class Person(nm:String) {
    private val name:String=nm
}
val fred = new Person("Fred")
...

and tried to remove @serializable and some other permutations. 并试图删除@serializable和其他一些排列。 The file "test.obj" is created, over 2Kb in size and has plausible contents. 创建文件“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). 欢迎使用Scala版本2.10.3(Java HotSpot(TM)64位服务器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> import java.io._ import java.io._

scala> val fis = new FileInputStream( "test.obj" ) fis: java.io.FileInputStream = java.io.FileInputStream@716ad1b3 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 oin = new ObjectInputStream(fis)oin:java.io.ObjectInputStream = java.io.ObjectInputStream@1f927f0a

scala> val p= oin.readObject java.io.WriteAbortedException: writing aborted; scala> val p = oin.readObject java.io.WriteAbortedException:write 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.loadAn java.io.NotSerializableException:主要$$匿名$ 1日java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)在java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)在java.io.ObjectInputStream.readSerialData( ObjectInputStream.java:1915)java.io.ObjectInputStream.readObject(ObjectInputStream.java:1780)java.io.ObjectInputStream.readObject上的java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) )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.loadAn dRunReq$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 dRunReq $ 1(IMain.scala:573)位于scala的scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)。 tools.nsc.interpreter.ILoop.reallyInterpret $ 1(ILoop.scala:756)在scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)在scala.tools.nsc.interpreter.ILoop.command(ILOOP .scala:713)scala.tools.nsc.interpreter.ILoop.processLine $ 1(ILoop.scala:577)在scala.tools的scala.tools.nsc.interpreter.ILoop.innerLoop $ 1(ILoop.scala:584)。 nsc.interpreter.ILoop.loop(ILoop.scala:587)位于scala.tools.nsc的scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply $ mcZ $ sp(ILoop.scala:878)。 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。在scala.tools.nsc.MainGenericRunner.runTarget的scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)上的util.ScalaClassLoader $ .savingContextLoader(ScalaClassLoader.scala:135) $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.DelegatingMe $ 1(MainGenericRunner.scala:83)在scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)在scala.tools.nsc.MainGenericRunner $。主要(MainGenericRunner.scala:105)在scala.tools.nsc。 MainGenericRunner.main(MainGenericRunner.scala)引起:java.io.NotSerializableException:java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)的Main $$ anon $ 1 1547)在java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)在java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)在java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)在java的。 io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)在Main $$ anon $ 1。(a.scala:11)位于Main.main(a.scala:1)的Main $ .main(a.scala:1)位于sun。 sun.reflect.DelegatingMe的sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)中的reflect.NativeMethodAccessorImpl.invoke0(Native Method) thodAccessorImpl.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 sca thodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)在java.lang.reflect.Method.invoke(Method.java:606)在scala.tools.nsc.util.ScalaClassLoader $$ anonfun $运行$ 1.适用(ScalaClassLoader.scala: 71)在scala.tools.nsc.util.ScalaClassLoader $ class.asContext(ScalaClassLoader.scala:31)在scala.tools.nsc.util.ScalaClassLoader $ URLClassLoader.asContext(ScalaClassLoader.scala:139)在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)scala.tools.nsc.ObjectRunner上的scala.tools.nsc.ObjectRunner $ .run(ObjectRunner.scala:45)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在sca .apply(ScriptRunner.scala:188) la.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.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply $ mcZ $ sp(ScriptRunner.scala:157)at la.tools.nsc.ScriptRunner $$ anonfun $ runScript $ 1.apply(ScriptRunner.scala:188) scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply(ScriptRunner.scala:131)位于scala.tools的scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply(ScriptRunner.scala:131)。 nsc.util.package $ .trackingThreads(package.scala:51)at scala.tools.nsol.pack. $ www.waitingForThreads(package.scala:35)scala.tools.nsc.ScriptRunner.withCompiledScript(ScriptRunner.scala: 130)在scala.tools.nsc.ScriptRunner.runScriptAndCatch(ScriptRunner.scala:201)的scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:188)scala.tools.nsc.MainGenericRunner.runTarget $ 1(MainGenericRunner。斯卡拉:76)......还有3个

Note that @serializable scaladoc tells that it is deprecated since 2.9.0: 请注意, @serializable scaladoc告诉它自2.9.0以来已弃用:

Deprecated (Since version 2.9.0) instead of @serializable class C, use class C extends Serializable 不推荐使用(从版本2.9.0开始)而不是@serializable类C,使用类C扩展Serializable

So you just have to use Serializable trait: 所以你只需要使用Serializable特性:

class Person(val age: Int) extends Serializable

This works for me (type :paste in 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. 所以,你可以看到,[de]序列化尝试成功了。

Edit (on why you're getting NotSerializableException when you run Scala script from file) 编辑(从文件运行Scala脚本时为什么会出现NotSerializableException

I've put my code into a file and tried to run it via scala test.scala and got exactly the same error as you. 我已经将我的代码放入一个文件并试图通过scala test.scala运行它并得到与你完全相同的错误。 Here is my speculation on why it happens. 这是我对其发生原因的猜测。

According to the stack trace a weird class Main$$anon$1 is not serializable. 根据堆栈跟踪,一个奇怪的类Main$$anon$1不可序列化。 Logical question is: why it is there in the first place? 逻辑问题是:为什么它首先出现在那里? We're trying to serialize Person after all, not something weird. 我们毕竟试图序列化Person ,而不是奇怪的东西。

Scala script is special in that it is implicitly wrapped into an object called Main . Scala脚本的特殊之处在于它隐式包装在名为Main的对象中。 This is indicated by the stack trace: 这由堆栈跟踪指示:

at Main$$anon$1.<init>(test.scala:9)
at Main$.main(test.scala:1)
at Main.main(test.scala)

The names here suggest that Main.main static method is the program entry point, and this method delegates to Main$.main instance method ( object 's class is named after the object but with $ appended). 这里的名称表明Main.main静态方法是程序入口点,并且此方法委托给Main$.main实例方法( object的类以对象命名,但附加$ )。 This instance method in turn tries to create an instance of a class Main$$anon$1 . 此实例方法依次尝试创建Main$$anon$1类的实例。 As far as I remember, anonymous classes are named that way. 据我所知,匿名类是以这种方式命名的。

Now, let's try to find exact Person class 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$1$Person

This means that Person is not a top-level class; 这意味着Person不是顶级课程; instead it is a nested class defined in the anonymous class generated by the compiler! 相反,它是由编译器生成的匿名类中定义的嵌套类! So in fact we have something like this: 所以事实上我们有这样的事情:

object Main {
  def main(args: Array[String]) {
    new {  // this is where Main$$anon$1 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. 但是在Scala中,所有嵌套类都是Java中的“嵌套非静态”(或“内部”)类。 This means that these classes always contain an implicit reference to an instance of their enclosing class. 这意味着这些类总是包含对其封闭类的实例的隐式引用 In this case, enclosing class is Main$$anon$1 . 在这种情况下,封闭类是Main$$anon$1 Because of that when Java serializer tries to serialize Person , it transitively encounters an instance of Main$$anon$1 and tries to serialize it, but since it is not Serializable , the process fails. 因此,当Java序列化程序尝试序列化Person ,它会传递地遇到Main$$anon$1的实例并尝试序列化它,但由于它不是Serializable ,因此该过程失败。 BTW, serializing non-static inner classes is a notorious thing in Java world, it is known to cause problems like this one. 顺便说一句,序列化非静态内部类是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. 至于为什么它在REPL中工作,似乎在REPL声明的类中某种程度上不会最终成为内部类,因此它们没有任何隐含字段。 Hence serialization works normally for them. 因此序列化通常适用于它们。

You could use the Serializable Trait: 您可以使用Serializable Trait:

Trivial Serialization example using Java Serialization with the Serializable Trait: 使用带有Seri​​alizable 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-Pickling进行序列化

https://github.com/scala/pickling or part of the Standard-Distribution starting with Scala 2.11 https://github.com/scala/pickling或标准分发的一部分,以Scala 2.11开头

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. 在exmple代码中,对象不会写入文件,而是使用JSON而不是ByteCode Serialization,这避免了源自不同Scala版本之间字节代码不兼容的某些问题。

import scala.pickling._
import json._

case class Person(age: Int)

val person = Person(31)
val pickledPerson = person.pickle
val unpickledPerson = pickledPerson.unpickle[Person]

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 age is simply discarded and Person has no member fields. 请注意,参数age被简单地丢弃,而Person没有成员字段。

You want either: 你想要:

  1. @serializable case class Person(age:Int)

  2. @serializable class Person(val age:Int)

You can leave out the empty curly brackets at the end. 您可以在末尾省略空的花括号。 In fact, it's encouraged. 事实上,它受到了鼓励。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM