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

如何在SharedPreferences中保存类的树形结构?

解决带循环引用的Item类序列化/持久化问题

你的Item类存在循环引用(父Item包含子Item,子Item的parent又指向父Item),这就是常规序列化、Parcelable方案失效的核心原因——序列化时会触发无限递归,最终导致栈溢出或无效数据。下面针对不同场景给出实用解决方案:

方案1:修复Parcelable实现(跨组件传递用)

不要直接序列化parent字段,改为只序列化树结构,反序列化后手动重建父引用:

data class Item(
    var name: String,
    var children: MutableList<Item> = mutableListOf(),
    @Transient var parent: Item? = null // 标记为不参与序列化
) : Parcelable {

    constructor(parcel: Parcel) : this(
        parcel.readString()!!,
        mutableListOf<Item>().apply {
            parcel.readTypedList(this, Item.CREATOR)
        }
        // 不读取parent,反序列化后手动设置
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeTypedList(children)
        // 不写入parent,避免循环递归
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Item> {
        override fun createFromParcel(parcel: Parcel): Item {
            val item = Item(parcel)
            // 反序列化后自动给子项设置父引用
            item.children.forEach { child ->
                child.parent = item
            }
            return item
        }

        override fun newArray(size: Int): Array<Item?> {
            return arrayOfNulls(size)
        }
    }
}

方案2:Kotlinx Serialization(序列化/持久化用)

用@Transient忽略parent字段,反序列化后通过递归修复引用:

import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
data class Item(
    var name: String,
    var children: MutableList<Item> = mutableListOf(),
    @Transient var parent: Item? = null // 跳过序列化
)

// 序列化函数
fun serializeItem(root: Item): String {
    return Json.encodeToString(root)
}

// 反序列化并修复父引用
fun deserializeItem(json: String): Item {
    val root = Json.decodeFromString<Item>(json)
    // 递归遍历所有子项,设置parent
    fun fixParent(item: Item, parent: Item?) {
        item.parent = parent
        item.children.forEach { fixParent(it, item) }
    }
    fixParent(root, null)
    return root
}

方案3:Gson序列化(兼容旧项目)

用@Expose标记parent不参与序列化,反序列化后修复引用:

import com.google.gson.Gson
import com.google.gson.annotations.Expose

data class Item(
    var name: String,
    var children: MutableList<Item> = mutableListOf(),
    @Expose(serialize = false, deserialize = false) var parent: Item? = null
)

fun serializeWithGson(root: Item): String {
    return Gson().toJson(root)
}

fun deserializeWithGson(json: String): Item {
    val root = Gson().fromJson(json, Item::class.java)
    fun fixParent(item: Item, parent: Item?) {
        item.parent = parent
        item.children.forEach { fixParent(it, item) }
    }
    fixParent(root, null)
    return root
}

核心思路总结

  • 循环引用是问题根源,必须避免序列化parent字段
  • 序列化仅保存树的结构(name + children)
  • 反序列化/传递完成后,通过递归遍历手动重建父引用,既规避循环,又保留完整的树结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 00:25:27