Menyesuaikan kunci pengkodean secara manual
Dalam contoh Anda, Anda mendapatkan kesesuaian yang dibuat secara otomatis Codablekarena semua properti Anda juga mematuhi Codable. Kesesuaian ini secara otomatis membuat jenis kunci yang hanya sesuai dengan nama properti - yang kemudian digunakan untuk menyandikan ke / mendekode dari satu wadah kunci.
Namun satu fitur yang benar-benar rapi dari kesesuaian yang dihasilkan secara otomatis ini adalah jika Anda mendefinisikan sebuah bertingkat enumdalam tipe Anda yang disebut " CodingKeys" (atau menggunakan a typealiasdengan nama ini) yang sesuai dengan CodingKeyprotokol - Swift akan secara otomatis menggunakan ini sebagai tipe kunci. Oleh karena itu, hal ini memungkinkan Anda untuk dengan mudah menyesuaikan kunci yang properti Anda dienkode / didekodekan.
Artinya, Anda cukup mengatakan:
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
}
}
Nama kasus enum harus cocok dengan nama properti, dan nilai mentah kasus ini harus cocok dengan kunci yang Anda encoding / dekode dari (kecuali ditentukan lain, nilai mentah Stringenumerasi akan sama dengan nama kasus ). Oleh karena itu, zipproperti sekarang akan dienkode / didekodekan menggunakan kunci "zip_code".
Aturan yang tepat untuk otomatis Encodable/ Decodablekesesuaian dirinci oleh proposal evolusi (penekanan saya):
Selain CodingKeysintesis persyaratan otomatis untuk
enums, Encodable& Decodablepersyaratan juga dapat disintesis secara otomatis untuk jenis tertentu:
Jenis yang sesuai dengan Encodablepropertinya semuanya Encodablemendapatkan properti pemetaan enum yang Stringdidukung secara otomatis CodingKeyke nama kasus. Begitu pula untuk Decodabletipe yang propertinya semuanyaDecodable
Jenis yang termasuk dalam (1) - dan jenis yang secara manual menyediakan CodingKey enum(dinamai CodingKeys, secara langsung, atau melalui a typealias) yang kasusnya memetakan 1-ke-1 ke Encodable/ Decodableproperti berdasarkan nama - dapatkan sintesis otomatis init(from:)dan encode(to:)jika sesuai, menggunakan properti dan kunci tersebut
Jenis yang tidak termasuk dalam (1) maupun (2) harus menyediakan jenis kunci khusus jika diperlukan dan menyediakannya sendiri init(from:)dan
encode(to:), sebagaimana mestinya
Contoh pengkodean:
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"}
Contoh decoding:
// 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")
snake_caseKunci JSON otomatis untuk camelCasenama properti
Di Swift 4.1, jika Anda mengganti nama zipproperti menjadi zipCode, Anda dapat memanfaatkan strategi encoding / decoding kunci pada JSONEncoderdan JSONDecoderuntuk secara otomatis mengonversi kunci pengkodean antara camelCasedan snake_case.
Contoh pengkodean:
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"}
Contoh decoding:
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")
Namun, satu hal penting yang perlu diperhatikan tentang strategi ini adalah bahwa ia tidak akan dapat mengubah beberapa nama properti dengan akronim atau inisialisme yang, menurut pedoman desain API Swift , harus sama besar atau kecilnya (tergantung pada posisinya). ).
Misalnya, properti bernama someURLakan dienkode dengan kunci tersebut some_url, tetapi saat mendekode, ini akan diubah menjadi someUrl.
Untuk memperbaikinya, Anda harus secara manual menentukan kunci pengkodean untuk properti itu menjadi string yang diharapkan decoder, misalnya someUrldalam kasus ini (yang masih akan diubah some_urloleh encoder):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Ini tidak menjawab pertanyaan spesifik Anda secara ketat, tetapi mengingat sifat kanonik T&J ini, saya merasa ini layak untuk disertakan)
Pemetaan kunci JSON otomatis kustom
Di Swift 4.1, Anda dapat memanfaatkan strategi pengkodean / dekode kunci khusus JSONEncoderdan JSONDecoder, memungkinkan Anda menyediakan fungsi khusus untuk memetakan kunci pengkodean.
Fungsi yang Anda sediakan mengambil a [CodingKey], yang mewakili jalur pengkodean untuk titik saat ini dalam pengkodean / dekode (dalam banyak kasus, Anda hanya perlu mempertimbangkan elemen terakhir; yaitu, kunci saat ini). Fungsi mengembalikan a CodingKeyyang akan menggantikan kunci terakhir dalam larik ini.
Misalnya, UpperCamelCasekunci JSON untuk lowerCamelCasenama properti:
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
}
}
}
Anda sekarang dapat melakukan enkode dengan .convertToUpperCamelCasestrategi utama:
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"}
dan memecahkan kode dengan .convertFromUpperCamelCasestrategi kunci:
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; bisakah saya mencantumkan satu kunci yang saya ubah?