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

Swift中JSON编解码Project结构体后UUID不一致的原因与解决

问题

编写了将Project结构体以JSON格式保存到磁盘的代码,但发现将结构体实例编码后再解码,得到的id和id2的UUID与原实例不一致。

代码示例

import Foundation
import PlaygroundSupport
import Swift

struct Project: Codable, Identifiable, Hashable  {
    let id: UUID = UUID()
    let id2: String = UUID().uuidString 
    let name: String
}

let project = Project(name: "PP50")
let encodedData = try JSONEncoder().encode(project)
let jsonString = String(data: encodedData,
                        encoding: .utf8)

print(project.id)
print(project.id2)
print(project.name)
print(jsonString)
print("===================")

if let dataFromJsonString = jsonString?.data(using: .utf8) {
    let projectFromData = try JSONDecoder().decode(Project.self, from: dataFromJsonString)
    print(projectFromData)
    print(projectFromData.name)
    print(projectFromData.id) // <<<< why is the id and id'2 different
    print(projectFromData.id2)
}

运行结果

826FEC8A-7FE0-4E6C-8DC1-A0DFA4FC75BE
2D15A1B8-B936-4829-8DC2-9C44B261712E
PP50
Optional("{\"id\":\"826FEC8A-7FE0-4E6C-8DC1-A0DFA4FC75BE\",\"id2\":\"2D15A1B8-B936-4829-8DC2-9C44B261712E\",\"name\":\"PP50\"}")
===================
Project(id: E951D459-3C0E-43A3-B9C6-2CCF2EB1AEE3, id2: "3C97B5C3-054D-4372-800D-9210439F48C2", name: "PP50")
PP50
E951D459-3C0E-43A3-B9C6-2CCF2EB1AEE3
3C97B5C3-054D-4372-800D-9210439F48C2

原因分析

你给id和id2设置了默认属性值,每次创建Project实例时(包括解码过程中),系统都会执行UUID()和UUID().uuidString生成新的UUID,覆盖JSON里原本存储的值。

具体逻辑:

  • 编码时,project实例的id和id2会被正确序列化到JSON中;
  • 解码时,JSONDecoder会先创建一个新的Project实例,此时默认值会先生成新UUID,之后才会尝试给属性赋值——但因为id和id2是let常量,赋值操作无法覆盖已生成的默认值,最终导致解码后的实例使用了新的UUID。

解决方法

要让解码时使用JSON中存储的UUID值,需要去掉默认属性值,改为在初始化时生成UUID,同时保证Codable能正确解码这两个属性。

方案一:自定义初始化方法(推荐)

去掉默认值,只保留手动初始化方法用于主动创建实例,Codable会自动合成解码初始化逻辑:

struct Project: Codable, Identifiable, Hashable  {
    let id: UUID
    let id2: String
    let name: String
    
    // 主动创建实例时生成UUID
    init(name: String) {
        self.id = UUID()
        self.id2 = UUID().uuidString
        self.name = name
    }
}

这样:

  • 主动创建实例时,通过init(name:)生成UUID;
  • 解码时,JSONDecoder会自动读取JSON中的值给id和id2赋值,不会生成新UUID。

方案二:使用lazy属性(不推荐)

如果一定要保留类似默认值的写法,可以把id和id2改为var并标记为lazy,但这样会失去属性的不可变性,可能引发意外行为:

struct Project: Codable, Identifiable, Hashable  {
    lazy var id: UUID = UUID()
    lazy var id2: String = UUID().uuidString 
    let name: String
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:52:46