Scala中结构相似对象序列化结果不同问题求助
Scala嵌套结构序列化问题排查
问题背景
在Spark DataFrame创建场景中遇到序列化异常:两个结构相似的对象,一个可正常序列化,另一个无法序列化,已定位到Row对象的嵌套结构存在不可序列化问题。
排查代码
try { val byteOut = new ByteArrayOutputStream() val objOut = new ObjectOutputStream(byteOut) objOut.writeObject(p) objOut.close() sys.error("Serialization successful") } catch { case e: Exception => e.printStackTrace() }
实例对比
可序列化的p实例
Record( Map( language -> Project(Variable(x1,RecordCType(Map(language -> StringType, info -> BagCType(RecordCType(Map(users -> StringType, difficulty -> IntType, average_review -> DoubleType)))))),language), info -> Project(Variable(x1,RecordCType(Map(language -> StringType, info -> BagCType(RecordCType(Map(users -> StringType, difficulty -> IntType, average_review -> DoubleType)))))),info)))
不可序列化的p实例
Record( Map( lName -> Project(Variable(x5,RecordCType(Map(s1_index -> LongType, nested -> BagCType(RecordCType(Map(users -> IntType, inUse -> BoolType))), userNo -> IntType, lName -> StringType))),lName), nested -> Project(Variable(x5,RecordCType(Map(s1_index -> LongType, nested -> BagCType(RecordCType(Map(users -> IntType, inUse -> BoolType))), userNo -> IntType, lName -> StringType))),nested)))
错误栈信息
- object not serializable (class: scala.collection.immutable.MapLike$$anon$2, value: Map(users -> IntType, inUse -> BoolType))
相关类定义
case class Record(fields: Map[String, CExpr]) extends CExpr { def tp: RecordCType = { RecordCType(fields.map(f => f._1 -> f._2.tp)) } } case class Project(e1: CExpr, field: String) extends CExpr case class Variable(name: String, override val tp: Type) extends CExpr final case class BagCType(tp: Type) extends Type final case class RecordCType(attrTps: Map[String, Type]) extends Type sealed trait Type extends Serializable trait CExpr { self => def tp: Type def vstr: String = self.toString val isCacheUnfriendly: Boolean = false }
问题分析与解决方向
核心原因
栈跟踪指向的MapLike$$anon$2是Scala中映射的匿名视图子类,而非标准的可序列化Map实现(如HashMap)。这种匿名子类通常由mapValues、filterKeys等返回视图的操作生成,不支持Java序列化。
对比两个实例的Map差异:
- 可序列化的Map包含3个键值对,Scala默认生成具体的
HashMap实例(元素数量少于阈值时会使用可序列化的具体子类); - 不可序列化的Map仅2个键值对,大概率是通过视图操作生成的匿名子类,而非可序列化的具体Map实现。
排查与修复建议
- 强制转换为具体Map实现:在构造
RecordCType时,将传入的Map显式转换为HashMap或ListMap,比如:// 修改RecordCType构造逻辑 final case class RecordCType(attrTps: Map[String, Type]) extends Type { private val serializableAttrs = attrTps.to(collection.immutable.HashMap) } // 或者在创建时直接转换 RecordCType(Map(users -> IntType, inUse -> BoolType).to(HashMap)) - 检查Map生成方式:确认不可序列化实例中的Map是否来自
mapValues/filterKeys等视图操作,替换为map等返回具体Map的操作; - 验证基础Type的序列化:确保
IntType、BoolType等所有Type子类都正确实现Serializable(case class默认支持,但需确认自定义基础类型是否遗漏); - 分段序列化测试:拆分嵌套结构,分步序列化
RecordCType、BagCType等组件,精准定位故障节点。
内容的提问来源于stack exchange,提问作者B-Brennan
相关产品推荐
相关产品推荐

