反序列化在Scala 2.10中引发'ClassNotFoundException:JavaConversions $ SeqWrapper' [英] Deserialization throws 'ClassNotFoundException: JavaConversions$SeqWrapper' in Scala 2.10

查看:296
本文介绍了反序列化在Scala 2.10中引发'ClassNotFoundException:JavaConversions $ SeqWrapper'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个从Scala-2.9序列化的相当复杂的对象图,我需要将其读入Scala-2.10.但是,在对象图Scala-2.10中的某个地方会抛出:

I have a fairly complex object graph serialized out from Scala-2.9 and I need to read it into Scala-2.10. However, somewhere deep in the object graph Scala-2.10 throws:

! 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

What is the simplest way to load this serialized object into Scala-2.10? The object deserializes correctly with Scala-2.9, but looks like things have moved around in the standard library. Most of the members of scala.collection.JavaConversions are now in scala.collection.convert.Wrappers

展望未来,我也对更持久地保存大型复杂对象图而不必为涉及的每个类明确指定序列化的更健壮的方式感兴趣.

Going forward, I am also interested in more robust ways of persisting large complex object graphs without having to explicitly specify the serialization for every class involved.

推荐答案

由于@ som-snytt为我指明了正确的方向,所以这才是对我有用的东西

So here is what ended up working for me, thanks to @som-snytt for pointing me in the right direction:

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中的包装器类与相应的类具有不同的SerialVersionUID在2.9的scala.collection.JavaConversions中,即使源在文本上是相同的.我最初试图重定向到scala.collection.convert.Wrappers并使用Javassist设置SerialVersionUID:

This allows me to read in my original 2.9 serialized files directly into 2.10 without re-serialization. It depends on Javassist to do the Class indirection and uses ClassLoaderObjectStream from Apache Commons, even though that would be simple to roll your own. I am not that happy that I had to make my own copy of SeqWrapper (this turned out to be the only offending class in my file), but the wrapper classes in scala-2.10's scala.collection.convert.Wrappers have different SerialVersionUIDs than the corresponding classes in 2.9's scala.collection.JavaConversions even though the sources are textually identical. I originally tried to just redirect to scala.collection.convert.Wrappers and set the SerialVersionUID with Javassist:

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的方法,而又不破坏其他任何内容,我想听听.

That allowed me to read the serialized file without exception, but the object that was read in that way was incomplete. (If this approach worked and there was more than one problem class in the file, I would really need a lookup table for the SerialVersionUIDs, but that is beside the point). If anybody knows a way set the SerialVersionUID on the Javassist generated class without breaking anything else something else I would like to hear it.

这篇关于反序列化在Scala 2.10中引发'ClassNotFoundException:JavaConversions $ SeqWrapper'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆