反序列化抛出 'ClassNotFoundException的:JavaConversion

2019-10-18 06:33发布

我从斯卡拉 - 2.9序列化出一个相当复杂的对象图,我需要读取到斯卡拉2.10。 然而,深某处对象图斯卡拉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]
...

什么是这个序列化对象加载到斯卡拉2.10最简单的方法? 对象使用Scala-2.9正确反序列化,但看起来事情在标准库中已经搬来搬去。 大多数成员scala.collection.JavaConversions现在在scala.collection.convert.Wrappers

展望未来,我无需显式地指定为每个参与类的序列化也有兴趣在持续大量复杂的对象图的更稳健的方式。

Answer 1:

思想没有真正的帮助,:

  1. 你碰到了对的Scala集合在底层实现这一变化体现在相同的序列化。 您无法“只是加载”到2.10,所以你需要一些共同点。

  2. 你很可能会使用Scala的每一个版本上运行这个,因为收藏还没有完全解决。

  3. 我相信你的意图是加载使用2.9基于代码的图形,转换成一些新的格式,并在新的“普通”格式倾倒。

  4. 在Java世界中,我会达到JAXB或SDO; 也许是的EclipseLink莫西 。 我怀疑莫西都知道斯卡拉集合类型。

  5. 我相信你已经看到了这个 。

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



Answer 2:

请不要downvote作为一个下意识的,但我的想法是在一个类加载器(上斯卡拉2.9)反序列化,转换成Java集合,然后在第二类加载器(与2.10的类路径上)从Java转换回到阶。

换句话说,Java是通用格式(Java运行时是常见的两种类加载器)。

(或者,而不是两个类加载器,尝试序列化java的形式,然后斜切回来。)

我觉得这正好给理查德#6。 它不必是完全Java核心,只有什么是兼容的。

我会尽力一间休息的时候拿出一个例子,但当然,需要注意的是,该不兼容在于收集和不收集的东西。



Answer 3:

这可能会炸毁你的脸,但你可以尝试把2.9斯卡拉-library.jar放在classpath 2.10斯卡拉 - library.jar之后。 然后,类加载器应该找到

 scala.collection.JavaConversions$SeqWrapper

,但如果序列化去,一路过关斩将我会感到惊讶?

你应该考虑只用序列化或GSON杰克逊或升JSON或任何以JSON。 这是乏味的,但你这样做一次,它的完成,它会与其他语言工作过 - 你的“快速”解决方案将意味着经常性疼痛向前......

祝好运!

鲁本



Answer 4:

所以这是最终为我工作,感谢@ 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做类和间接使用ClassLoaderObjectStream Apache的风景,即使这是简单推出自己的。 我不是那快乐的,我不得不让我自己SeqWrapper的副本(这竟然是在我的文件中唯一有问题的类),但在斯卡拉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()
          }
    }
  }
}

这让我读无一例外的序列化的文件,但被以这种方式读取的对象是不完整的。 (如果这种方式工作,有不止一个问题类的文件,我真的需要为SerialVersionUIDs查找表,但这是题外话)。 如果有人知道的方式设置的serialVersionUID在了Javassist生成的类不破坏任何其他别的东西,我想听到它。



文章来源: Deserialization throws 'ClassNotFoundException: JavaConversions$SeqWrapper' in Scala 2.10