Scala 2.12反序列化Scala 2.11序列化的Symbol兼容性问题
问题场景
代码示例:
import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream} object SymbolSerializeDemo { def main(args: Array[String]): Unit = { val fileName = "file.ser" val symbolCheck: Symbol = Symbol("someSymbol") //serializeToFile(symbolCheck, fileName) deserializeFromFile(fileName) } private def serializeToFile(input: Symbol, fileName: String): Unit = { try { val file: FileOutputStream = new FileOutputStream(fileName) val out: ObjectOutputStream = new ObjectOutputStream(file) out.writeObject(input) } } private def deserializeFromFile(fileName: String): Unit = { try { val file: FileInputStream = new FileInputStream(fileName) val input: ObjectInputStream = new ObjectInputStream(file) val output = input.readObject.asInstanceOf[Symbol] println("Symbol after deseralization " + output.name) } } }
在Scala 2.12中反序列化Scala 2.11序列化的scala.Symbol对象时,抛出异常:
java.io.InvalidClassException: scala.Symbol; local class incompatible: stream classdesc serialVersionUID = 2966401305346518859, local class serialVersionUID = 6865603221856321286
尝试为自定义类及Symbol添加serialVersionUID无效,因为Symbol是Scala标准库类,无法修改其内部定义。
解决方案
方案1:替换序列化逻辑(推荐)
Symbol的核心是其name字符串,序列化时直接存储字符串,反序列化时重新创建Symbol对象,从根源避免版本兼容问题:
修改后的代码:
import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream} object SymbolSerializeDemo { def main(args: Array[String]): Unit = { val fileName = "file.ser" val symbolCheck: Symbol = Symbol("someSymbol") // 序列化时存字符串 serializeSymbolByName(symbolCheck, fileName) // 反序列化时重建Symbol val restoredSymbol = deserializeSymbolFromName(fileName) println("Symbol after deserialization: " + restoredSymbol.name) } private def serializeSymbolByName(symbol: Symbol, fileName: String): Unit = { val file = new FileOutputStream(fileName) val out = new ObjectOutputStream(file) try { out.writeUTF(symbol.name) } finally { out.close() file.close() } } private def deserializeSymbolFromName(fileName: String): Symbol = { val file = new FileInputStream(fileName) val in = new ObjectInputStream(file) try { val name = in.readUTF() Symbol(name) } finally { in.close() file.close() } } }
方案2:修复已有序列化文件的反序列化
如果已经有大量用Scala 2.11序列化的Symbol文件,需要在Scala 2.12中读取,可以自定义ObjectInputStream,重写resolveClass方法,忽略serialVersionUID的差异:
import java.io.{FileInputStream, ObjectInputStream, ObjectStreamClass} private class CompatibleSymbolInputStream(filePath: String) extends ObjectInputStream(new FileInputStream(filePath)) { override def resolveClass(desc: ObjectStreamClass): Class[_] = { if (desc.getName == classOf[Symbol].getName) { // 强制使用本地的Symbol类,忽略serialVersionUID差异 classOf[Symbol] } else { super.resolveClass(desc) } } } // 修改原反序列化方法 private def deserializeFromFile(fileName: String): Unit = { val in = new CompatibleSymbolInputStream(fileName) try { val output = in.readObject().asInstanceOf[Symbol] println("Symbol after deserialization: " + output.name) } finally { in.close() } }
注意:这种方法依赖Symbol类的内部结构在2.11到2.12之间没有发生破坏性变化(实际上Scala 2.11到2.12的Symbol内部主要是serialVersionUID变化,字段还是只有name),所以可以正常读取。但如果后续版本Symbol结构变化,这种方法可能失效。
内容的提问来源于stack exchange,提问作者user3771712
相关产品推荐
相关产品推荐

