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

Kotlin数据类中Duration成员反序列化异常问题排查

问题分析与解决方案

首先,你的代码存在一个明显的语法错误:次级构造函数调用主构造函数时多传了一个参数。主构造函数仅包含3个参数(Long、String、Duration),但你写的this(0, "", Duration.ZERO, "")多了一个空字符串,会导致编译失败,先修正为:

@Serializable
data class DataClass(
    val fieldOne: Long,
    val fieldTwo: String,
    val fieldThree: Duration,
) {
    constructor() : this(0, "", Duration.ZERO)
}

接下来是核心的反序列化问题:
你遇到的fieldThree反序列化后变为0的问题,本质是Firebase Realtime Database的默认反序列化机制与kotlinx.serialization的配合冲突,或者你没有让Firebase正确识别Duration类型的序列化逻辑。

关键原因

Firebase Realtime Database默认使用基于Java反射的序列化/反序列化(遵循JavaBean规范),而非直接调用kotlinx.serialization的逻辑。即便你添加了@Serializable注解,Firebase默认也不会采用它处理对象。反序列化时,Firebase会先调用无参构造函数创建实例(此时fieldThree被设为Duration.ZERO),再尝试通过反射为字段赋值——但Firebase默认不识别Duration类型,无法将数据库中存储的毫秒数值转换为Duration对象,最终fieldThree会保持构造函数里的ZERO值。

解决方案

方案1:让Firebase使用kotlinx.serialization处理序列化/反序列化

为Firebase配置自定义转换器,将所有序列化/反序列化逻辑交给kotlinx.serialization的Json处理:

import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.ValueEventListener
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

// 序列化:将对象转为JSON字符串存入Firebase
fun <T> serializeToFirebase(value: T, serializer: kotlinx.serialization.KSerializer<T>): String {
    return Json.encodeToString(serializer, value)
}

// 反序列化:从Firebase读取JSON字符串转为对象
fun <T> deserializeFromFirebase(json: String, serializer: kotlinx.serialization.KSerializer<T>): T {
    return Json.decodeFromString(serializer, json)
}

// 存储数据示例
val data = DataClass(12345, "", Duration.hours(1))
val json = Json.encodeToString(DataClass.serializer(), data)
firebaseDatabaseReference.setValue(json)

// 读取数据示例
firebaseDatabaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val json = snapshot.getValue(String::class.java) ?: ""
        val data = Json.decodeFromString(DataClass.serializer(), json)
        // 此时data.fieldThree会是正确的1小时时长
    }

    override fun onCancelled(error: DatabaseError) {
        // 处理错误逻辑
    }
})

这种方式下,Firebase仅存储JSON字符串,所有类型转换逻辑由kotlinx.serialization处理,能完美支持Duration类型。

方案2:为Firebase自定义Duration类型的TypeAdapter

若希望继续使用Firebase的默认对象存储方式(不转JSON字符串),可以为Duration编写Firebase兼容的TypeAdapter,让Firebase能正确转换时长与毫秒数值:

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import java.time.Duration

class DurationTypeAdapter : TypeAdapter<Duration>() {
    override fun write(out: JsonWriter, value: Duration?) {
        if (value == null) {
            out.nullValue()
        } else {
            out.value(value.toMillis())
        }
    }

    override fun read(`in`: JsonReader): Duration {
        val millis = `in`.nextLong()
        return Duration.ofMillis(millis)
    }
}

然后在数据类的fieldThree字段上添加@JsonAdapter注解(需引入Gson依赖,Firebase默认用Gson处理序列化):

import com.google.gson.annotations.JsonAdapter
import java.time.Duration
import kotlinx.serialization.Serializable

@Serializable
data class DataClass(
    val fieldOne: Long,
    val fieldTwo: String,
    @JsonAdapter(DurationTypeAdapter::class)
    val fieldThree: Duration,
) {
    constructor() : this(0, "", Duration.ZERO)
}

这样Firebase反序列化时,会调用自定义的TypeAdapter将数据库中的毫秒数转换为Duration对象,不再保持ZERO值。

额外注意点

  • 确保kotlinx.serialization配置正确:在build.gradle中添加kotlinx-serialization-core依赖,并启用序列化插件。
  • 避免同时混用kotlinx.serialization与Firebase默认序列化机制,选一种方式保持一致,防止冲突。

内容的提问来源于stack exchange,提问作者tomerpacific

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:00:36