Tùy chỉnh thủ công các khóa mã hóa
Trong ví dụ của bạn, bạn đang nhận được một quy tắc được tạo tự động Codablevì tất cả các thuộc tính của bạn cũng tuân theo Codable. Sự tuân thủ này tự động tạo ra một loại khóa đơn giản tương ứng với tên thuộc tính - sau đó được sử dụng để mã hóa / giải mã từ một vùng chứa có khóa duy nhất.
Tuy nhiên một thực sự tính năng gọn gàng của sự phù hợp tự động tạo ra này là nếu bạn định nghĩa một lồng enumtrong loại của bạn được gọi là " CodingKeys" (hoặc sử dụng một typealiasvới tên này) mà phù hợp với những CodingKeygiao thức - Swift sẽ tự động sử dụng này làm loại khóa. Do đó, điều này cho phép bạn dễ dàng tùy chỉnh các khóa mà thuộc tính của bạn được mã hóa / giải mã.
Vì vậy, điều này có nghĩa là bạn chỉ có thể nói:
struct Address : Codable {
    var street: String
    var zip: String
    var city: String
    var state: String
    private enum CodingKeys : String, CodingKey {
        case street, zip = "zip_code", city, state
    }
}
Tên trường hợp enum cần phải khớp với tên thuộc tính và giá trị thô của những trường hợp này cần khớp với các khóa mà bạn đang mã hóa / giải mã từ đó (trừ khi được chỉ định khác, các giá trị thô của kiểu Stringliệt kê sẽ giống như tên trường hợp ). Do đó, thuộc ziptính bây giờ sẽ được mã hóa / giải mã bằng cách sử dụng khóa "zip_code".
Các quy tắc chính xác cho sự tuân thủ Encodable/ được tạo tự động Decodableđược nêu chi tiết bởi đề xuất phát triển (tôi nhấn mạnh):
  Ngoài việc CodingKeytổng hợp yêu cầu tự động cho
   enums, Encodable& Decodablecác yêu cầu cũng có thể được tổng hợp tự động cho một số loại nhất định:
  
  
  - Các kiểu tuân theo - Encodablethuộc tính của chúng đều- Encodablenhận được thuộc tính ánh xạ enum-- Stringbacked được tạo tự động- CodingKeyvới tên trường hợp. Tương tự đối với- Decodablecác loại có thuộc tính là- Decodable
 
- Các loại thuộc (1) - và các loại cung cấp thủ công - CodingKey- enum(được đặt tên- CodingKeys, trực tiếp hoặc thông qua a- typealias) có các trường hợp ánh xạ từ 1 đến 1 đến- Encodable/- Decodablethuộc tính theo tên - nhận tổng hợp tự động- init(from:)và- encode(to:)khi thích hợp, sử dụng các thuộc tính và khóa đó
 
- Các loại không thuộc (1) và (2) sẽ phải cung cấp loại khóa tùy chỉnh nếu cần và cung cấp loại khóa riêng - init(from:)và- encode(to:)nếu thích hợp
 
Mã hóa mẫu:
import Foundation
let address = Address(street: "Apple Bay Street", zip: "94608",
                      city: "Emeryville", state: "California")
do {
    let encoded = try JSONEncoder().encode(address)
    print(String(decoding: encoded, as: UTF8.self))
} catch {
    print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
Giải mã ví dụ:
// using the """ multi-line string literal here, as introduced in SE-0168,
// to avoid escaping the quotation marks
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
    let decoded = try JSONDecoder().decode(Address.self, from: Data(jsonString.utf8))
    print(decoded)
} catch {
    print(error)
}
// Address(street: "Apple Bay Street", zip: "94608",
// city: "Emeryville", state: "California")
Các snake_casekhóa JSON tự động cho camelCasetên thuộc tính
Trong Swift 4.1, nếu bạn đổi tên thuộc tính của mình zipthành zipCode, bạn có thể tận dụng các chiến lược mã hóa / giải mã khóa trênJSONEncoder và JSONDecoderđể tự động chuyển đổi khóa mã hóa giữa camelCasevà snake_case.
Mã hóa mẫu:
import Foundation
struct Address : Codable {
  var street: String
  var zipCode: String
  var city: String
  var state: String
}
let address = Address(street: "Apple Bay Street", zipCode: "94608",
                      city: "Emeryville", state: "California")
do {
  let encoder = JSONEncoder()
  encoder.keyEncodingStrategy = .convertToSnakeCase
  let encoded = try encoder.encode(address)
  print(String(decoding: encoded, as: UTF8.self))
} catch {
  print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
Giải mã ví dụ:
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""
do {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
  print(decoded)
} catch {
  print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
Tuy nhiên, một điều quan trọng cần lưu ý về chiến lược này là nó sẽ không thể đảo ngược một số tên thuộc tính bằng các từ viết tắt hoặc chữ viết tắt, theo  nguyên tắc thiết kế API Swift , phải là chữ hoa hoặc chữ thường (tùy thuộc vào vị trí ).
Ví dụ: một thuộc tính có tên someURL sẽ được mã hóa bằng khóa some_url, nhưng khi giải mã, thuộc tính này sẽ được chuyển thành someUrl.
Để khắc phục điều này, bạn sẽ phải chỉ định thủ công khóa mã hóa cho thuộc tính đó thành chuỗi mà bộ giải mã mong đợi, ví dụ: someUrltrong trường hợp này (sẽ vẫn được some_urlbộ mã hóa chuyển đổi thành ):
struct S : Codable {
  private enum CodingKeys : String, CodingKey {
    case someURL = "someUrl", someOtherProperty
  }
  var someURL: String
  var someOtherProperty: String
}
(Điều này không trả lời chính xác câu hỏi cụ thể của bạn, nhưng với bản chất chính tắc của Câu hỏi và Đáp này, tôi cảm thấy nó đáng để bao gồm)
Ánh xạ khóa JSON tự động tùy chỉnh
Trong Swift 4.1, bạn có thể tận dụng các chiến lược mã hóa / giải mã khóa tùy chỉnh trên JSONEncodervàJSONDecoder , cho phép bạn cung cấp một chức năng tùy chỉnh để ánh xạ các khóa mã hóa.
Hàm bạn cung cấp nhận một [CodingKey], đại diện cho đường dẫn mã hóa cho điểm hiện tại trong mã hóa / giải mã (trong hầu hết các trường hợp, bạn sẽ chỉ cần xem xét phần tử cuối cùng; nghĩa là khóa hiện tại). Hàm trả về a CodingKeysẽ thay thế khóa cuối cùng trong mảng này.
Ví dụ: UpperCamelCasecác khóa JSON cho lowerCamelCasetên thuộc tính:
import Foundation
// wrapper to allow us to substitute our mapped string keys.
struct AnyCodingKey : CodingKey {
  var stringValue: String
  var intValue: Int?
  init(_ base: CodingKey) {
    self.init(stringValue: base.stringValue, intValue: base.intValue)
  }
  init(stringValue: String) {
    self.stringValue = stringValue
  }
  init(intValue: Int) {
    self.stringValue = "\(intValue)"
    self.intValue = intValue
  }
  init(stringValue: String, intValue: Int?) {
    self.stringValue = stringValue
    self.intValue = intValue
  }
}
extension JSONEncoder.KeyEncodingStrategy {
  static var convertToUpperCamelCase: JSONEncoder.KeyEncodingStrategy {
    return .custom { codingKeys in
      var key = AnyCodingKey(codingKeys.last!)
      // uppercase first letter
      if let firstChar = key.stringValue.first {
        let i = key.stringValue.startIndex
        key.stringValue.replaceSubrange(
          i ... i, with: String(firstChar).uppercased()
        )
      }
      return key
    }
  }
}
extension JSONDecoder.KeyDecodingStrategy {
  static var convertFromUpperCamelCase: JSONDecoder.KeyDecodingStrategy {
    return .custom { codingKeys in
      var key = AnyCodingKey(codingKeys.last!)
      // lowercase first letter
      if let firstChar = key.stringValue.first {
        let i = key.stringValue.startIndex
        key.stringValue.replaceSubrange(
          i ... i, with: String(firstChar).lowercased()
        )
      }
      return key
    }
  }
}
Bây giờ bạn có thể mã hóa bằng .convertToUpperCamelCasechiến lược chính:
let address = Address(street: "Apple Bay Street", zipCode: "94608",
                      city: "Emeryville", state: "California")
do {
  let encoder = JSONEncoder()
  encoder.keyEncodingStrategy = .convertToUpperCamelCase
  let encoded = try encoder.encode(address)
  print(String(decoding: encoded, as: UTF8.self))
} catch {
  print(error)
}
//{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
và giải mã bằng .convertFromUpperCamelCasechiến lược chính:
let jsonString = """
{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
"""
do {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromUpperCamelCase
  let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
  print(decoded)
} catch {
  print(error)
}
// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")
               
              
CodingKeysenum; Tôi có thể liệt kê một khóa mà tôi đang thay đổi không?