如何在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
相关产品推荐
相关产品推荐

