如何从 iOS Swift Codable 中的 API 响应通知或打印模型类上缺少的密钥?

Posted

技术标签:

【中文标题】如何从 iOS Swift Codable 中的 API 响应通知或打印模型类上缺少的密钥?【英文标题】:How to notify or print for missing key on model class from API response in iOS Swift Codable? 【发布时间】:2019-12-20 17:38:45 【问题描述】:

我有一个来自 API 的 JSON 响应,如下所示,

先前的 JSON 响应:

[
  
    "EmployeeId": 711,
    "FirstName": "Steve",
    "LastName": "Jobs"
  ,
  
    "EmployeeId": 714,
    "FirstName": "John",
    "LastName": "Doe"
  
]

同样的模型类有以下代码

class EmployeeModel: Codable 

    let EmployeeId: Int?
    let FirstName: String?
    let LastName: String?

用于解析 Swift Codable 工作正常

do 
    let decodedResponse = try JSONDecoder().decode([EmployeeModel].self, from: response.rawData())
    print(decodedResponse)

 catch let jsonErr 
    print(jsonErr.localizedDescription)

但现在

最新的 JSON 响应

更改了来自 API,并添加了一个 MiddleName 键作为响应,参见以下屏幕截图,它也可以与 Swift Codable 代码一起正常工作。

但是,我如何才能在 iOS Swift 5 中的 API 的 JSON 响应中获得通知或打印现在添加的 MiddleName 键?

问题更新

根据@CZ54 下面提供的答案,解决方案工作正常,但无法检查另一个派生类是否缺少密钥。例如:

// MARK:- LoginModel
class LoginModel: Codable 

    let token: String?
    let currentUser: CurrentUser?


// MARK:- CurrentUser
class CurrentUser: Codable 

    let UserName: String?
    let EmployeeId: Int?
    let EmployeeName: String?
    let CompanyName: String?

【问题讨论】:

没有报错,解析成功.. 当您有可用的“新”属性时,您会尝试收到警告? 我只是想在我的 Xcode 控制台或任何其他方式上通知或打印丢失的键 @CZ54 你能告诉我工作答案如何实现吗? 这是您需要与主要拥有/发布该 API 的人进行的讨论,也许他们会以某种方式传达他们的更改。 【参考方案1】:

您可以执行以下操作:

let json = """
    
        "name" : "Jobs",
        "middleName" : "Bob"
    
"""


class User: Decodable 
    let name: String

extension JSONDecoder 
    func decodeAndCheck<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable  
       let result = try self.decode(type, from: data)

        if let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: Any] 
            let mirror = Mirror(reflecting: result)
            let jsonKeys = json.map  return $0.0 
            let objectKeys = mirror.children.enumerated().map  $0.element.label 

            jsonKeys.forEach  (jsonKey) in
                if !objectKeys.contains(jsonKey) 
                    print("\(jsonKey) is not used yet")
                
            
        
        return result

    

try JSONDecoder().decodeAndCheck(User.self, from: json.data(using: .utf8)!)

//will print "middleName is not use yet"

【讨论】:

非常好的解决方案。但是如果检查失败,你应该抛出 DecodingError 以及 JSONSerialization 错误 这不是最初的要求,但可以改进 @CZ54 非常感谢,就我的问题而言,工作正常,但是嵌套 Codable 类有什么解决方案吗?例如:让部门:部门? where class Department: Codable let deptId: Int?让部门名称:字符串?如果响应位置键将添加到部门模型类中,则上述解决方案可能不起作用,在这种情况下也请帮助 @CZ54 您现在可以查看我更新的问题吗? 这不是一个非常有用的解决方案,它仅适用于***对象,因此如果您有一组对象或子对象,它将失败。如果您使用CodingKey 修改属性名称,它也会失败。所以总而言之,这是一个非常有限的解决方案【参考方案2】:

您可以实现initializer并检查key是否存在,如下所示,

struct CustomCodingKey: CodingKey 

    let intValue: Int?
    let stringValue: String

    init?(stringValue: String) 
        self.intValue = Int(stringValue)
        self.stringValue = stringValue
    

    init?(intValue: Int) 
        self.intValue = intValue
        self.stringValue = "\(intValue)"
    


class EmployeeModel: Codable 

    var EmployeeId: Int?
    var FirstName: String?
    var LastName: String?

    private enum CodingKeys: String, CodingKey 
        case EmployeeId, FirstName, LastName
    

    required init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let allKeysContainer = try decoder.container(keyedBy: CustomCodingKey.self)

        self.EmployeeId = try container.decodeIfPresent(Int.self, forKey: .EmployeeId)
        self.FirstName = try container.decodeIfPresent(String.self, forKey: .FirstName)
        self.LastName = try container.decodeIfPresent(String.self, forKey: .LastName)

        let objectKeys = container.allKeys.map  $0.rawValue
        for key in allKeysContainer.allKeys 
            if objectKeys.contains(key.stringValue) == false 
                print("Key: " + key.stringValue + " is added!")
            
        
    

【讨论】:

如果进一步添加更多键值对会怎样?问题是关于识别 API 中所做的更改。 这就是问题所在。这就是 OP 想知道的。 @Kamran 不是这样,请再次参考问题以获得更好的理解。

以上是关于如何从 iOS Swift Codable 中的 API 响应通知或打印模型类上缺少的密钥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Codable 使用 Swift 归档

Swift IOS 无法使用 Codable 访问 json 数组数据

Swift4 中的 Codable 和 XMLParser

如何从 Swift Codable 中排除属性?

iOS swift Codable 不能与 Alamofire 一起使用 JSON 嵌套数据?

如何在 Swift 中使用 Codable 转换带有可选小数秒的日期字符串?