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?"""
adalah untuk literal multi-baris :)"
s: DCodable
sebaliknya)Address
tidak perlu mengikat diri Anda sendiri untuk mendekode objek JSON yang dimulai di tempat tertentu dalam grafik objek induk. Akan jauh lebih baik untuk mengabstraksi jalur kunci awal hingga dekoder itu sendiri - berikut adalah implementasi hackey-ish yang kasar .Dengan Swift 4.2, sesuai dengan kebutuhan Anda, Anda dapat menggunakan salah satu dari 3 strategi berikut untuk membuat nama properti khusus objek model Anda cocok dengan kunci JSON Anda.
# 1. Menggunakan kunci pengkodean khusus
Saat Anda mendeklarasikan struct yang sesuai dengan
Codable
(Decodable
danEncodable
protokol) dengan implementasi berikut ...... kompilator secara otomatis menghasilkan enum bersarang yang sesuai dengan
CodingKey
protokol untuk Anda.Oleh karena itu, jika kunci yang digunakan dalam format data serial tidak cocok dengan nama properti dari tipe data Anda, Anda dapat mengimplementasikan enum ini secara manual dan menyetel yang sesuai
rawValue
untuk kasus yang diperlukan.Contoh berikut menunjukkan bagaimana melakukannya:
Encode (mengganti
zip
properti dengan kunci JSON "zip_code"):Decode (mengganti kunci JSON "zip_code" dengan
zip
properti):# 2. Menggunakan strategi kode kasus ular untuk kasus unta
Jika JSON Anda memiliki tombol ular-cased dan Anda ingin mengkonversikannya ke sifat unta-cased untuk objek model Anda, Anda dapat mengatur Anda
JSONEncoder
'skeyEncodingStrategy
danJSONDecoder
' skeyDecodingStrategy
properti untuk.convertToSnakeCase
.Contoh berikut menunjukkan bagaimana melakukannya:
Enkode (mengonversi properti selubung unta menjadi kunci JSON bersarung ular):
Dekode (mengonversi kunci JSON yang disimpan dalam kotak ular menjadi properti kasing unta):
# 3. Menggunakan strategi pengkodean kunci khusus
Jika perlu,
JSONEncoder
danJSONDecoder
memungkinkan Anda untuk mengatur strategi kustom untuk memetakan kunci pengkodean menggunakanJSONEncoder.KeyEncodingStrategy.custom(_:)
danJSONDecoder.KeyDecodingStrategy.custom(_:)
.Contoh berikut menunjukkan cara menerapkannya:
Encode (mengonversi properti huruf pertama huruf kecil menjadi huruf besar pertama kunci JSON):
Dekode (mengubah kunci JSON huruf besar pertama menjadi properti huruf pertama huruf kecil):
Sumber:
sumber
Apa yang telah saya lakukan adalah membuat struktur sendiri seperti yang Anda dapatkan dari JSON sehubungan dengan tipe datanya.
Seperti ini:
Setelah ini, Anda perlu membuat ekstensi yang sama
struct
memperluasdecodable
danenum
struktur yang sama denganCodingKey
dan kemudian Anda perlu untuk menginisialisasi decoder menggunakan enum ini dengan tombol dan tipe data (Keys akan datang dari enum dan tipe data akan datang atau katakanlah dirujuk dari struktur itu sendiri)Anda perlu mengubah di sini setiap kunci dan tipe data sesuai dengan kebutuhan Anda dan menggunakannya dengan decoder.
sumber
Dengan menggunakan CodingKey, Anda dapat menggunakan kunci khusus dalam protokol yang dapat dikodekan atau didekodekan.
sumber