1

我有一个从 Scala-2.9 序列化出来的相当复杂的对象图,我需要将它读入 Scala-2.10。然而,在对象图 Scala-2.10 深处的某个地方抛出:

! java.lang.ClassNotFoundException: scala.collection.JavaConversions$SeqWrapper
! at java.net.URLClassLoader$1.run(URLClassLoader.java:366) ~[na:1.7.0_21]
! at java.net.URLClassLoader$1.run(URLClassLoader.java:355) ~[na:1.7.0_21]
! at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_21]
! at java.net.URLClassLoader.findClass(URLClassLoader.java:354) ~[na:1.7.0_21]
! at java.lang.ClassLoader.loadClass(ClassLoader.java:423) ~[na:1.7.0_21]
! at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) ~[na:1.7.0_21]
! at java.lang.ClassLoader.loadClass(ClassLoader.java:356) ~[na:1.7.0_21]
! at java.lang.Class.forName0(Native Method) ~[na:1.7.0_21]
! at java.lang.Class.forName(Class.java:266) ~[na:1.7.0_21]
! at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:623) ~[na:1.7.0_21]
...

将此序列化对象加载到 Scala-2.10 中的最简单方法是什么?该对象使用 Scala-2.9 正确反序列化,但看起来事情在标准库中已经移动。现在大部分成员scala.collection.JavaConversions都在scala.collection.convert.Wrappers

展望未来,我还对持久化大型复杂对象图的更健壮的方法感兴趣,而不必为所涉及的每个类显式指定序列化。

4

4 回答 4

2

没有真正帮助的想法:

  1. 您遇到了 Scala 集合的底层实现的变化,这反映在相同的序列化中。您无法将其“加载”到 2.10,因此您需要一些共同点。

  2. 你可能会在每个版本的 Scala 中遇到这个问题,因为 Collections 还没有完全解决。

  3. 我认为您的意图是使用基于 2.9 的代码加载您的图表,转换为某种新格式,然后以新的“通用”格式转储。

  4. 在 Java 世界中,我会使用 JAXB 或 SDO;也许是EclipseLink MOXy。我怀疑 MOXy 会知道 Scala 集合类型。

  5. 我想你已经看过这个了

  6. 您的对象图可以转换为完全基于核心 Java 数据类型的东西吗?

于 2013-06-24T16:16:31.087 回答
1

请不要下意识地投票,但我的想法是在一个类加载器中反序列化(在 scala 2.9 上),转换为 java 集合,然后在第二个类加载器(类路径上为 2.10)从 java 转换回到斯卡拉。

换句话说,java 是通用格式(java 运行时对两个类加载器都是通用的)。

(或者,尝试序列化 Java 表单,而不是两个类加载器,然后将其转回。)

我认为这与理查德的#6有关。它不必完全是 java 核心,只要是兼容的即可。

我会尝试在喝咖啡的时候举一个例子,但当然需要注意的是,不兼容在于集合而不是集合的内容。

于 2013-06-26T22:25:03.833 回答
0

这可能会让您大吃一惊,但您可以尝试将 2.9 scala-library.jar 放在类路径中,放在 2.10 scala-library.jar 之后。然后类加载器应该找到

 scala.collection.JavaConversions$SeqWrapper

,但如果序列化一直进行,我会感到惊讶......

您应该考虑只使用 gson 或 jackson 或 lift-json 或其他任何方式序列化为 json。这很乏味,但你只做一次,它就完成了,它也可以与其他语言一起使用——你的“快速”解决方案将意味着不断出现的痛苦……

祝你好运!

鲁本

于 2013-06-26T15:23:01.697 回答
0

所以这最终对我有用,感谢@som-snytt 为我指明了正确的方向:

object MyWrappers {
  import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc }
  import scala.collection.convert._
  import WrapAsScala._
  import WrapAsJava._
  import Wrapper._

  @SerialVersionUID(3200663006510408715L)
  case class SeqWrapper[A](underlying: Seq[A]) extends ju.AbstractList[A] with Wrappers.IterableWrapperTrait[A] {
    def get(i: Int) = underlying(i)
  }
}

import org.apache.commons.io.input.ClassLoaderObjectInputStream
object Loader extends ClassLoader {
  override def loadClass(name: String) : Class[_] = {
    import javassist._
    try super.loadClass(name)
    catch {
      case e: ClassNotFoundException if name.startsWith("scala.collection.JavaConversions") => {
            val name2 = name.replaceFirst("scala.collection.JavaConversions",
                                          "MyWrappers")
            val cls = ClassPool.getDefault().getAndRename(name2, name)
            cls.toClass()
          }
    }
  }
}

val objectStream = new ClassLoaderObjectInputStream(Loader, stream)
objectStream.readObject()

这使我可以将原始 2.9 序列化文件直接读入 2.10,而无需重新序列化。它依赖于 Javassist 来做类间接并使用来自 Apache Commons 的 ClassLoaderObjectStream,尽管这很容易让你自己滚动。我很不高兴我必须制作自己的 SeqWrapper 副本(结果证明这是我文件中唯一有问题的类),但是 scala-2.10 中的包装类scala.collection.convert.Wrappers与 2.9 中的相应类具有不同的 SerialVersionUID sscala.collection.JavaConversions即使来源在文本上是相同的。我最初尝试只是重定向到 scala.collection.convert.Wrappers 并使用 Javassist 设置 SerialVersionUID:

object Loader extends ClassLoader {
  override def loadClass(name: String) : Class[_] = {
    import javassist._
    try super.loadClass(name)
    catch {
      case e: ClassNotFoundException if name.startsWith("scala.collection.JavaConversions") => {
            val name2 = name.replaceFirst("JavaConversions", "convert.Wrappers")
            val cls = ClassPool.getDefault().getAndRename(name2, name)
            cls.addField(CtField.make("private static final long serialVersionUID = 3200663006510408715L;", cls))
            cls.toClass()
          }
    }
  }
}

这使我可以毫无例外地读取序列化文件,但是以这种方式读取的对象是不完整的。(如果这种方法有效并且文件中存在多个问题类,我真的需要一个 SerialVersionUID 的查找表,但这不是重点)。如果有人知道在 Javassist 生成的类上设置 SerialVersionUID 而不破坏其他任何东西的方法,我想听听。

于 2013-06-29T21:01:52.153 回答