如何对包含object类型的Kotlin密封类进行序列化/反序列化?
Kotlin密封类(含单例Object)的Genson序列化与反序列化方案
嘿,这个问题我碰到过!Genson对Kotlin数据类的支持确实不错,但单例object确实是个小坑——毕竟Genson默认会尝试用反射创建实例,而单例根本没有公共构造函数。不过咱们通过自定义转换器加上合理的多态配置就能完美解决,下面一步步来:
1. 先把依赖配好
首先确保你引入了Genson的核心库和Kotlin扩展模块,比如Gradle项目里:
implementation 'com.owlike:genson:1.6' implementation 'com.owlike:genson-kotlin:1.6'
Maven项目的话,对应引入这两个依赖即可。
2. 为单例Object写自定义转换器
针对你的Loading单例,我们需要写一个Genson的Converter,负责它的序列化和反序列化逻辑——序列化时只标记类型,反序列化时直接返回单例实例:
import com.owlike.genson.Context import com.owlike.genson.Converter import com.owlike.genson.stream.ObjectReader import com.owlike.genson.stream.ObjectWriter object LoadingConverter : Converter<ViewModel.Loading> { override fun serialize(obj: ViewModel.Loading, writer: ObjectWriter, ctx: Context) { // 序列化时只写入类型标识,因为单例没有其他属性 writer.beginObject() writer.writeString("type", "Loading") writer.endObject() } override fun deserialize(reader: ObjectReader, ctx: Context): ViewModel.Loading { // 反序列化时直接返回单例,同时要把流里的内容读完避免报错 reader.beginObject() while (reader.hasNext()) { reader.next() reader.skipValue() } reader.endObject() return ViewModel.Loading } }
3. 配置Genson实例,整合多态与单例支持
接下来要创建一个自定义配置的Genson实例,既要启用Kotlin支持,还要配置多态类型识别,同时注册咱们的单例转换器:
import com.owlike.genson.Genson import com.owlike.genson.GensonBuilder val genson: Genson = GensonBuilder() // 启用Genson的Kotlin扩展,处理数据类、空值等特性 .useKotlin() // 开启多态类型元数据支持,指定用"type"字段存储类型标识 .setWithClassMetadata(true) .setClassMetadataFieldName("type") // 为Loading单例注册自定义转换器 .registerConverter(ViewModel.Loading::class.java, LoadingConverter) // 映射类型别名:让Genson知道"Loaded"对应Loaded数据类,"Loading"对应Loading单例 .addAlias("Loaded", ViewModel.Loaded::class.java) .addAlias("Loading", ViewModel.Loading::class.java) .create()
这里的关键是setClassMetadataFieldName("type")——它告诉Genson在序列化时把类型信息写到type字段里,反序列化时通过这个字段判断具体类型。而addAlias则是把友好的类型名称和实际类绑定起来。
4. 测试序列化与反序列化
现在就可以用这个配置好的genson实例来处理你的密封类实例了:
序列化测试
// 序列化Loaded数据类实例 val loaded = ViewModel.Loaded("Hello Kotlin") val loadedJson = genson.serialize(loaded) println(loadedJson) // 输出结果:{"type":"Loaded","value":"Hello Kotlin"} // 序列化Loading单例 val loadingJson = genson.serialize(ViewModel.Loading) println(loadingJson) // 输出结果:{"type":"Loading"}
反序列化测试
// 反序列化Loaded val deserializedLoaded = genson.deserialize(loadedJson, ViewModel::class.java) println(deserializedLoaded is ViewModel.Loaded) // 输出true println((deserializedLoaded as ViewModel.Loaded).value) // 输出Hello Kotlin // 反序列化Loading,并且验证是同一个单例 val deserializedLoading = genson.deserialize(loadingJson, ViewModel::class.java) println(deserializedLoading is ViewModel.Loading) // 输出true println(deserializedLoading === ViewModel.Loading) // 输出true,证明是同一个单例实例
额外小贴士
- 如果你的密封类后续新增了其他单例子类,只需要为每个单例写类似的转换器,然后在Genson配置里注册并添加别名即可。
- 确保
addAlias里的名称和序列化时的type字段值完全一致,否则反序列化会找不到对应类型。
内容的提问来源于stack exchange,提问作者Zbigniew Malinowski
相关产品推荐
相关产品推荐

