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?