Как различить при декодировании с использованием Codable Protocol в Swift? - PullRequest
1 голос
/ 04 июля 2019

Я использую кодируемый протокол Swift.Я поделился кодом.

Я хочу, чтобы переменная boss в классе Employee получала тип на основе строки personType в классе Person.Я хочу использовать personType в качестве дискриминатора.Ответ, поступающий с сервера, будет каждый раз отличаться в зависимости от значения personType.

В классе Employee я объявил переменную boss с типом Person.Я хочу, чтобы он декодировался для типа Employee, если строка personType в классе Person является «Employee», и декодируется для типа Boss, если строка personType - «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.Если это был «Сотрудник», он должен автоматически декодироваться как «Сотрудник» или, если он равен нулю, должен декодироваться как «Персона».

{ 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 }

Ответы [ 2 ]

1 голос
/ 06 июля 2019

Необходимо изменить модель в соответствии с текущим опубликованным вами ответом

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 в нескольких свойствах, поскольку оно равно нулю согласно текущему ответу

при декодировании вы можете использовать разные вещи, я использовал 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

0 голосов
/ 06 июля 2019

Я бы добавил оператор switch в конце init(from decoder:) в Employee классе

    switch personType {
    case "Boss":
         boss = try container.decode(Boss.self, forKey: .boss)
    case "Employee":
        boss = try container.decode(Employee.self, forKey: .boss)
    default:
        boss = nil
    }
...