问题
I'm trying to understand the following issue that occurs when trying to serialize/deserialize a very simple data structure:
case class SimpleClass(i: Int)
object SerializationDebug {
def main(args: Array[String]) {
val c = SimpleClass(0)
val l1 = List(c)
serializationSaveToFile("test", l1)
val l2 = serializationLoadFromFile("test") // .asInstanceOf ...
}
def serializationSaveToFile(fn: String, o: Any) {
val fos = new FileOutputStream(fn)
val oos = new ObjectOutputStream(fos)
oos.writeObject(o)
oos.close()
}
def serializationLoadFromFile(fn: String): Any = {
val fis = new FileInputStream(fn)
val ois = new ObjectInputStream(fis)
return ois.readObject()
}
}
When trying to run this code I get java.lang.ClassNotFoundException: SimpleClass
in the deserialization step. The current results of my investigations are:
- The example works when I exchange
SimpleClass
by some built-in type, i.e., I can deserializeList[Int]
orList[(Int, Double)]
without a problem. Mixing built-in types with mySimpleClass
(i.e. having aList[Any]
) again throws an exception. - I tried to define
SimpleClass
in other scopes (for instance nested in the object or in the local scope even) but that did not change anything. Also, having a normal (non-case) class extendingSerializable
gives the same result. - Even more puzzling is that using an
Array[SimpleClass]
instead ofList
does work! Trying other containers confirms this strange inconsistency: havingSimpleClass
as type parameter within an immutable map works, in case of a mutable map I get the exception.
In case it matters: My Scala version is 2.10.0; JDK is 1.7.0.
What is going on here? Is this supposed to fail or is it some kind of bug? My actual problem at hand involves a much more complex data structure (a lot of nesting; mixture of built-in and own classes). Any suggestions to serializing/deserializing this data structure in a minimal-intrusive simple way (i.e. without having to find working combinations of container classes and their type parameters) are also welcome!
回答1:
This solution works fine for me:
val ois = new ObjectInputStream(new FileInputStream(fileName)) {
override def resolveClass(desc: java.io.ObjectStreamClass): Class[_] = {
try { Class.forName(desc.getName, false, getClass.getClassLoader) }
catch { case ex: ClassNotFoundException => super.resolveClass(desc) }
}
}
of course, when writing object, we use the same way:
val ois = new ObjectOutputStream(new FileOutputStream(path))
ois.writeObject(myobject)
回答2:
@trenobus @bluenote10 related to your findings:
I just ran into this today and I think it's because the class' name is different (mangled) when you define it in the console. For instance, I serialized a class One
from scala
> import java.io._
import java.io._
> case class One(s: String, b: Boolean)
defined class One
> new ObjectOutputStream(new FileOutputStream("data")) writeObject One("abc", true)
I then tried to deserialize it in java from the same file, having prepared a similar class named One
in the top level (I made SerialVersionUIDs be the same as well, didn't include it here since it didn't matter), but got this error:
Exception in thread "main" java.lang.ClassNotFoundException: $line4.$read$$iw$$iw$One
Suggesting that scala (rightfully) creates a new JVM class each time you define a class, and presumably keeps an internal mapping so that you can refer to it by its defined name (One
).
Similarly, because each such class is different in JVM, if you redefine class One
in that same scala console, and then try to deserialize from the data
file, you'll get an object of the original class (not of the new class that you redefined).
回答3:
Works if SimpleClass extends Serializable.
来源:https://stackoverflow.com/questions/16386252/scala-deserialization-class-not-found