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

