如何使用kotlinx.serialization序列化继承Java抽象类的Kotlin类
解决kotlinx.serialization继承Java抽象类的序列化问题
核心问题
kotlinx.serialization默认仅处理当前类标记为val/var的属性,不会自动识别并处理Java父类的私有字段。直接在Kotlin子类中重复声明同名属性会导致字节码中出现重复字段,进而引发序列化/反序列化逻辑混乱。
正确实现模式
方案1:自定义序列化器(无需修改Java父类)
通过实现KSerializer手动控制序列化和反序列化流程,直接操作Java父类的私有字段:
首先定义Kotlin子类B(不重复声明s属性):
@Serializable(with = BSerializer::class) class B(s: String) : A(s)
然后实现自定义序列化器:
object BSerializer : KSerializer<B> { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("B") { element<String>("s") } override fun serialize(encoder: Encoder, value: B) { // 通过反射读取Java父类的私有s字段 val field = A::class.java.getDeclaredField("s") field.isAccessible = true val sValue = field.get(value) as String encoder.encodeStringElement(descriptor, 0, sValue) } override fun deserialize(decoder: Decoder): B { val sValue = decoder.decodeStringElement(descriptor, 0) // 调用子类构造,传入s值初始化父类 return B(sValue) } }
方案2:给Java父类添加Getter(若可修改Java库)
如果能修改Java抽象类A,添加s的getter方法:
public abstract class A { private final String s; public A(String s) { this.s = s; } // 添加Getter方法 public String getS() { return s; } }
然后在Kotlin子类中利用Getter实现序列化,同时通过构造参数初始化父类:
@Serializable class B(s: String) : A(s) { // 序列化时读取父类的s值 @SerialName("s") @EncodeDefault private val serializedS: String get() = super.getS() }
这种方式无需编写完整自定义序列化器,让框架通过Getter读取父类字段,反序列化时直接通过构造参数完成父类初始化。
方案3:使用@Transient避免重复字段(简化版)
如果不想编写完整序列化器,可在Kotlin子类中声明s为val但标记@Transient,通过计算属性提供序列化所需值:
@Serializable data class B(@Transient val s: String) : A(s) { @SerialName("s") private val serializedS: String get() = s // 构造时已将s传入父类,此处值与父类s完全一致 }
该方式核心是利用Kotlin构造参数传递初始化父类,同时通过计算属性向序列化框架提供正确的s值,避免字节码中出现重复字段。
内容的提问来源于stack exchange,提问作者Max Rosoff
相关产品推荐
相关产品推荐

