[Swift] class 在繼承時如何正確使用 Encoder

在 Swift 中,只要繼承 Codable Protocol,就可以將 struct、enum 或是 class 做 Encoder,並變成一個 Json 物件或是 Dictionary,然而,如果 class 有使用繼承的話,事情就不是想的這麼簡單了。

swift 1

以下列例子為例,我們預期得到的答案會是「{“name”:”Sparky”,”age”:5,”breed”:”Labrador”}」,但是實際跑起來,卻是「{“name”:”Sparky”,”age”:5}」,為什麼呢?

class Animal: Codable {
  var name: String
  var age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }

  func speak() {
    print("\(name) says something")
  }
}


class Dog: Animal {
  var breed: String

  init(name: String, age: Int, breed: String) {
    self.breed = breed
    super.init(name: name, age: age)
  }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
     
  override func speak() {
    print("\(name) barks")
  }
}

let dog = Dog(name: "Sparky", age: 5, breed: "Labrador")

// 使用父類別的 JSONEncoder
let encoder = JSONEncoder()
let jsonData = try! encoder.encode(dog)
let jsonString = String(decoding: jsonData, as: UTF8.self)
print(jsonString)

原來是因為 Animal 實做 Codable,所以 Swift 只認得 Animal 的參數,並且將其 Encode,那麼我們如何將 Dog 的參數也 Encode 呢?很簡單,只要在 Dog 類別加入以下的程式碼。

private enum CodingKeys: String, CodingKey {
    case breed
}

override func encode(to encoder: Encoder) throws {
    try super.encode(to: encoder)
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(breed, forKey: .breed)
}

原始的程式如下,你會發現 Dog 也被 Encode 了

class Animal: Codable {
  var name: String
  var age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }

  func speak() {
    print("\(name) says something")
  }
}

class Dog: Animal {
    var breed: String

    init(name: String, age: Int, breed: String) {
        self.breed = breed
        super.init(name: name, age: age)
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
    
    private enum CodingKeys: String, CodingKey {
        case breed
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(breed, forKey: .breed)
    }
    
    override func speak() {
    print("\(name) barks")
    }
}

let dog = Dog(name: "Sparky", age: 5, breed: "Labrador")

// 使用父類別的 JSONEncoder
let encoder = JSONEncoder()
let jsonData = try! encoder.encode(dog)
let jsonString = String(decoding: jsonData, as: UTF8.self)
print(jsonString)
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments