Scala deserialization: class not found

点点圈 提交于 2019-12-12 10:48:17

问题


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 deserialize List[Int] or List[(Int, Double)] without a problem. Mixing built-in types with my SimpleClass (i.e. having a List[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 extending Serializable gives the same result.
  • Even more puzzling is that using an Array[SimpleClass] instead of List does work! Trying other containers confirms this strange inconsistency: having SimpleClass 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!