ios - 如何在 Swift 中使用 Codable 协议(protocol)进行解码时进行区分?

标签 ios swift codable

我正在使用 Swift 的 Codable 协议(protocol)。我已经分享了代码。

我想让Employee类中的boss变量根据Person类中的personTypeString获取类型。我想使用 personType 作为鉴别器。来自服务器的响应每次都会根据 personType 值而有所不同。

在 Employee 类中,我声明了 Person 类型的 boss 变量。如果 Person 类中的 personType 字符串是“Employee”,我希望它解码 Employee 类型,如果 personType 字符串是“Boss”,我希望它解码类型 Boss。如果它为 null,我只是希望它针对 Person 类型进行解码。

非常感谢任何帮助。

public class Person: Codable {

    public let address: String
    public let age: Int
    public let name: String
    public let uid: String
    public let personType: String?

    private enum CodingKeys: String, CodingKey {
        case address
        case age
        case name
        case uid
        case personType
    }

    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        address = try container.decode(String.self, forKey: .address)
        age = try container.decode(Int.self, forKey: .age)
        name = try container.decode(String.self, forKey: .name)
        uid = try container.decode(String.self, forKey: .uid)
        personType = try container.decodeIfPresent(String.self, forKey: .personType)
    }
}


public class Employee: Person {

    public let department: String
    public let dependents: [Person]?
    public let salary: Int
    public let workingDays: [Days]
    public var boss: Person?

    private enum CodingKeys: String, CodingKey {
        case department
        case dependents
        case salary
        case workingDays
        case boss
    }


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

        department = try container.decode(String.self, forKey: .department)
        dependents = try container.decode([Person].self, forKey: .dependents)
        salary = try container.decode(Int.self, forKey: .salary)
        workingDays = try container.decode([Days].self, forKey: .workingDays)
        boss = try container.decode(Person.self, forKey: .boss)
        try super.init(from: decoder)
    }

}



public class Boss: Employee {
    let promotedAt: Double
    let assistant: Employee?

    enum CodingKeys: String, CodingKey {
        case promotedAt
        case assistant
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        promotedAt = try container.decode(Double.self, forKey: .promotedAt)
        assistant = try container.decodeIfPresent(Employee.self, forKey: .assistant)
        try super.init(from: decoder)
    }
}

例如,在以下响应中,在老板部分,personType 设置为“老板”。所以应该解码为Boss类型。如果它是“Employee”,它应该自动解码为 Employee,或者如果它为 null,则应该解码为“Person”。

{ name: 'Shahid Khaliq',
  age: 5147483645,
  address: 'H # 531, S # 20',
  uid: '123321',
  salary: 20000,
  department: 'Software Development',
  workingDays: [ 'Monday', 'Tuesday', 'Friday' ],
  boss:
   { personType: 'Boss',
     assistant: null,
     name: 'Zeeshan Ejaz',
     age: 5147483645,
     address: 'H # 531, S # 20',
     uid: '123321',
     birthday: '1994-02-13',
     birthtime: '1994-02-13T14:01:54.000Z',
     salary: 20000,
     department: 'Software Development',
     joiningDay: 'Saturday',
     workingDays: [ 'Monday', 'Tuesday', 'Friday' ],
     dependents: null,
     hiredAt: 'Sun, 06 Nov 1994 08:49:37 GMT',
     boss: null,
     promotedAt: 1484719381 },
  dependents: null,
  hiredAt: 'Sun, 06 Nov 1994 08:49:37 GMT',
  personType: null }

最佳答案

需要根据您发布的当前响应更改模型

public class Employee: Person {

  public let department: String
  public let dependents: [Person]?
  public let salary: Int
  public let workingDays:[String] //[Days]
  public var boss: Person?

 private enum CodingKeys: String, CodingKey {
    case department
    case dependents
    case salary
    case workingDays
    case boss
 }


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

     department = try container.decode(String.self, forKey: .department)
    //dependents = try container.decode([Person].self, forKey: .dependents)
    dependents = try container.decodeIfPresent([Person].self, forKey: .dependents)
    salary = try container.decode(Int.self, forKey: .salary)
    // workingDays = try container.decode([Days].self, forKey: .workingDays)
    workingDays = try container.decode([String].self, forKey: .workingDays)
   // boss = try container.decode(Person.self, forKey: .boss)
    boss = try container.decodeIfPresent(Person.self, forKey: .boss)
    try super.init(from: decoder)
 }
}

这里在几个属性中添加了 decodeIfPresent,因为根据当前响应它是 null

在解码时你可以使用不同的东西,我使用 SwiftJSON 使代码更具可读性,

    // i have saved response in Response.JSON file so can change response as per need while testing below code.
    let file = Bundle.main.path(forResource: "Response", ofType: "JSON")
    let dataURL = URL(fileURLWithPath: file!)
    let data = try! Data(contentsOf: dataURL)
    let jsonData = try! JSON(data: data)
    //here JSON is struct which is part of SwiftyJSON

    print("jsondata \(jsonData)")
    do {

        let bossDict = jsonData["boss"]
        let dataBoss : Data = try! bossDict.rawData() 
        let bossType = bossDict["personType"].string
            if let type = bossType {
                if type == "Boss"{
                    let bossObj = try! JSONDecoder().decode(Boss.self, from: dataBoss)
                    print("boss name \(String(describing: bossObj.name))")
                    bossObj.listPropertiesWithValues()
                }else{
                    // type == "Employee"
                    let emplyeeObj = try! JSONDecoder().decode(Employee.self, from: dataBoss)
                    print("Employee name \(String(describing: emplyeeObj.name))")
                    emplyeeObj.listPropertiesWithValues()
                }
            }else{
                //type = nil

            }

    }catch{
        print("exception \(error)")
    }

您可以在链接下载相同的工作演示 DemoCodable

关于ios - 如何在 Swift 中使用 Codable 协议(protocol)进行解码时进行区分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56881912/

相关文章:

ios - 使用 Cordova 5 在 iOS 上拒绝白名单错误

android - 如何防止其他 iOS/Android 应用程序使用我的 RESTful API?

swift - 在 swift iOS 中出现意外值时,如何将 nil 设置为可编码枚举

swift - 可解码 JSONSerialization 错误自定义对象 alamofire

多个文件中具有 Codable 的属性包装器的 Swift 编译错误

ios - Swift 2 通过 Segue 从 UIcollectionview (indexpath.row) 传递数据

ios - GC Matchmaking邀请人能收到 "declined"消息吗?

swift - Apple ARKit 人脸识别项目 (ARKitFaceExample) 的运行时问题

ios - TableView 单元格错误 - iOS/Swift

ios - 无法在 UISearchController 上成为 FirstResponder 工作