从 JSON 解码 Codable 结构时,如何初始化 JSON 中不存在的 属性?

When decoding a Codable struct from JSON, how do you initialize a property not present in the JSON?

说在this example here中,这个结构

struct Reminder: Identifiable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
}

改为从 JSON 数组值解码(而不是像链接示例中那样构造)。如果每个 JSON 值都缺少一个“id”,那么 id 将如何初始化?我自己尝试时出现错误 keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").", underlyingError: nil)).

您可以简单地使用编码键,如下所示:

struct Reminder: Identifiable, Codable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
    
    enum CodingKeys: String, CodingKey {
        case title
        case dueDate
        case notes
        case isComplete
    }
}

这样,只有具有相应编码键的属性才会 encoded/decoded。

我自己的框架也遇到过这个问题ColorKit
我从 GitHub 下载了一个 450000 行 JSON 的数据库,发现它没有 id 而我的 SingleColor 模型是 Identifiable
啊啊啊啊
所以我花了几天时间将 id 一个一个地添加到它们中,并意识到我实际上可以创建一个没有 idstruct,称为 SingleColorInit 并加载JSON 到这个模型中,然后将我所有的数据转换到我的 SingleColor 模型中。
代码:

public struct SingleColorInit: Codable, Hashable{
    public var name: String
    public var hex: String
    public var rgb: RGBList
    public var hsl: HSLList
    public var luminance: Double
}
public struct SingleColor: Codable, Hashable, Identifiable{
    public var id: UUID = UUID()
    public var name: String
    public var hex: String
    public var rgb: RGBList
    public var hsl: HSLList
    public var luminance: Double
}

let coloKey: [InitColor] = load("database.json")
public func database() -> [SingleColor] {
    var returnData: [SingleColor] = []
    for datum in colorKey {
        returnData.append(SingleColor(name: datum.name, hex: datum.hex, rgb: datum.rgb, hsl: datum.hsl, luminance: datum.luminance))
    }
    returnData.remove(at: 0)
    return returnData
}

所以当我创建对象的新实例时,将生成 UUID。
在您的情况下,您可以这样做:

struct InitReminder {
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
}

struct Reminder: Identifiable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
}

// load your data (I use the load method in Apple's SwiftUI tutorial)

let reminders: [Reminder] = load("youdatabase.json")

func database() -> [Reminder] {
    var returnData: [Reminder] = []
    for datum in reminders{
        returnData.append(Reminder(title: datum.title, dueDate: datum.dueDate, notes: datum.notes, isComplete: datum.isComplete))
    }
    return returnData
}

然后我意识到...
当你在 SwiftUI 中使用你的数据时(如果你使用它)ListForEach,你不需要它是 Identifiable;使用每个项目的 self 作为 id。只需这样做:

List(yourData, id: \.self) { item in
   ...
}
ForEach(yourData, id: \.self) { item in
   ...
}

这个问题困扰了我很长时间,我真的很高兴与需要它的人分享这个解决方案
真烦人
希望对你有帮助

之前的回答不错

要获得完全控制,您需要覆盖默认解码。

struct Reminder: Identifiable {
    var id: String = UUID().uuidString
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
    
        id = (try? values.decode(String.self, forKey: .id)) ?? UUID()
        title = try values.decode(String.self, forKey: . title)
        dueDate = try values.decode(Date.self, forKey: . dueDate)
        notes = try? values.decode(String.self, forKey: .notes)
        isComplete = try values.decode(Bool.self, forKey: . isComplete)
    }
}

我问的有点过了,但是为什么 JSON 中没有设置 id

  • 如果 id 是数据库键,则它必须出现在 json 中。
  • 另一方面,如果 id 是识别 UI 中项目的唯一标识符,那么使用描述对象表示的专用对象会更安全、更干净。
struct ReminderItem: Identifiable {
   let id = UUID()
   let reminder: Reminder
}

顺便说一句,它的编码方式相同。

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types