我有一个从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
展望未来,我也对更强大的持久化大型复杂对象图的方法感兴趣,而无需为每个涉及的类明确指定序列化。
答案 0 :(得分:2)
没有真正帮助的想法:
您已经遇到了Scala集合的底层实现的更改,这些更改反映在相同的序列化中。你无法“加载”到2.10,所以你需要一些共同点。
您可能会遇到每个版本的Scala,因为收藏尚未完全解决。
我认为您的意图是使用基于2.9的代码加载图表,转换为某种新格式,并以新的“通用”格式转储。
在java世界中,我会选择JAXB或SDO;或许EclipseLink MOXy。我怀疑MOXy会意识到Scala集合类型。
我认为你已经看过this。
您的对象图是否可以完全基于核心Java数据类型转换为某些内容?
答案 1 :(得分:1)
请不要作为一个下意识的人投票,但我的想法是在一个类加载器(在scala 2.9上)反序列化,转换为java集合,然后在第二个类加载器(在类路径上使用2.10)从java转换回scala。
换句话说,java是常见的格式(java运行时对于两个类加载器都是通用的)。
(或者,不是两个类加载器,而是尝试序列化java表单,然后再回过头来。)
我认为这是理查德的#6。它不必完全是java核心,只有兼容的东西。
我会尝试在喝咖啡休息时提出一个例子,但当然需要注意的是,不相容性取决于收集而不是收集的内容。
答案 2 :(得分:0)
这可能会在你脸上爆炸,但你可以尝试在2.10 scala-library.jar之后将2.9 scala-library.jar放在你的类路径上。 然后类加载器应该找到
scala.collection.JavaConversions$SeqWrapper
,但如果序列化一直持续下去,我会感到惊讶......
您应该考虑使用gson或jackson或lift-json序列化为json 管他呢。这很乏味,但是你做了一次,它就完成了,它将会起作用 其他语言 - 你的“快” 解决方案将意味着反复出现的痛苦...
祝你好运!流便
答案 3 :(得分: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来实现Class间接并使用来自Apache Commons的ClassLoaderObjectStream,尽管这很简单。我不太高兴我必须制作自己的SeqWrapper副本(原来这是我文件中唯一有用的类),但scala-2.10的scala.collection.convert.Wrappers
中的包装类具有不同于它的SerialVersionUIDs。 2.9的scala.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而不打破其他任何我希望听到的东西。