You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 21:01:10