Menyesuaikan kunci pengkodean secara manual
Dalam contoh Anda, Anda mendapatkan kesesuaian yang dibuat secara otomatis Codable
karena 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 enum
dalam tipe Anda yang disebut " CodingKeys
" (atau menggunakan a typealias
dengan nama ini) yang sesuai dengan CodingKey
protokol - 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 String
enumerasi akan sama dengan nama kasus ). Oleh karena itu, zip
properti sekarang akan dienkode / didekodekan menggunakan kunci "zip_code"
.
Aturan yang tepat untuk otomatis Encodable
/ Decodable
kesesuaian dirinci oleh proposal evolusi (penekanan saya):
Selain CodingKey
sintesis persyaratan otomatis untuk
enums
, Encodable
& Decodable
persyaratan juga dapat disintesis secara otomatis untuk jenis tertentu:
Jenis yang sesuai dengan Encodable
propertinya semuanya Encodable
mendapatkan properti pemetaan enum yang String
didukung secara otomatis CodingKey
ke nama kasus. Begitu pula untuk Decodable
tipe 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
/ Decodable
properti 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_case
Kunci JSON otomatis untuk camelCase
nama properti
Di Swift 4.1, jika Anda mengganti nama zip
properti menjadi zipCode
, Anda dapat memanfaatkan strategi encoding / decoding kunci pada JSONEncoder
dan JSONDecoder
untuk secara otomatis mengonversi kunci pengkodean antara camelCase
dan 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 someURL
akan 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 someUrl
dalam kasus ini (yang masih akan diubah some_url
oleh 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 JSONEncoder
dan 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 CodingKey
yang akan menggantikan kunci terakhir dalam larik ini.
Misalnya, UpperCamelCase
kunci JSON untuk lowerCamelCase
nama 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 .convertToUpperCamelCase
strategi 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 .convertFromUpperCamelCase
strategi 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")
CodingKeys
enum; bisakah saya mencantumkan satu kunci yang saya ubah?