Personalizzazione manuale delle chiavi di codifica
Nel tuo esempio, stai ottenendo una conformità generata automaticamente a Codablecome sono conformi anche tutte le tue proprietà Codable. Questa conformità crea automaticamente un tipo di chiave che corrisponde semplicemente ai nomi delle proprietà, che viene quindi utilizzato per codificare / decodificare da un singolo contenitore con chiave.
Tuttavia uno davvero valida caratteristica di questa conformità generato automaticamente è che se si definisce un annidata enumnel vostro tipo chiamato " CodingKeys" (o utilizzare un typealiascon questo nome) conforme al CodingKeyprotocollo - Swift utilizzeranno automaticamente questo come il tipo di chiave. Ciò consente quindi di personalizzare facilmente le chiavi con cui vengono codificate / decodificate le proprietà.
Quindi questo significa che puoi semplicemente dire:
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
}
}
I nomi dei casi enum devono corrispondere ai nomi delle proprietà e i valori grezzi di questi casi devono corrispondere alle chiavi da cui stai codificando / decodificando (se non diversamente specificato, i valori grezzi di Stringun'enumerazione saranno gli stessi dei nomi dei casi ). Pertanto, la zipproprietà verrà ora codificata / decodificata utilizzando la chiave "zip_code".
Le regole esatte per l'auto-generazione Encodable/ Decodableconformità sono dettagliate dalla proposta di evoluzione (enfasi mia):
Oltre ad automatico CodingKeysintesi requisito
enums, Encodablee Decodablerequisiti possono essere sintetizzati automaticamente per determinati tipi come bene:
Tipi conformi alla Encodablecui proprietà sono tutti Encodableottenere un generate automaticamente Stringemessi a fronte di CodingKeyproprietà di mappatura enum ai nomi di casi. Allo stesso modo per i Decodabletipi le cui proprietà sono tutteDecodable
I tipi che rientrano in (1) - e i tipi che forniscono manualmente a CodingKey enum(denominato CodingKeys, direttamente o tramite a typealias) i cui casi mappano 1-a-1 a Encodable/ Decodableproprietà per nome - ottengono la sintesi automatica init(from:)e, encode(to:)se appropriato, utilizzando tali proprietà e chiavi
I tipi che non rientrano né in (1) né in (2) dovranno fornire un tipo di chiave personalizzato se necessario e fornire il proprio init(from:)e
encode(to:), a seconda dei casi
Codifica di esempio:
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"}
Esempio di decodifica:
// 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_caseChiavi JSON automatiche per camelCasei nomi delle proprietà
In Swift 4.1, se rinomini la tua zipproprietà in zipCode, puoi sfruttare le strategie di codifica / decodifica delle chiavi su JSONEncodere JSONDecoderper convertire automaticamente le chiavi di codifica tra camelCasee snake_case.
Codifica di esempio:
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"}
Esempio di decodifica:
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")
Una cosa importante da notare su questa strategia, tuttavia, è che non sarà in grado di eseguire il round trip di alcuni nomi di proprietà con acronimi o inizializzazioni che, secondo le linee guida di progettazione dell'API Swift , dovrebbero essere uniformemente maiuscole o minuscole (a seconda della posizione ).
Ad esempio, una proprietà denominata someURLverrà codificata con la chiave some_url, ma durante la decodifica, questa verrà trasformata in someUrl.
Per risolvere questo problema, dovrai specificare manualmente la chiave di codifica per quella proprietà come una stringa che il decoder si aspetta, ad esempio someUrlin questo caso (che verrà comunque trasformata some_urldal codificatore):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Questo non risponde rigorosamente alla tua domanda specifica, ma data la natura canonica di questa domanda e risposta, credo che valga la pena includerla)
Mappatura delle chiavi JSON automatica personalizzata
In Swift 4.1, puoi sfruttare le strategie di codifica / decodifica delle chiavi personalizzate su JSONEncodere JSONDecoder, che ti consentono di fornire una funzione personalizzata per mappare le chiavi di codifica.
La funzione che fornisci accetta a [CodingKey], che rappresenta il percorso di codifica per il punto corrente nella codifica / decodifica (nella maggior parte dei casi, dovrai solo considerare l'ultimo elemento, cioè la chiave corrente). La funzione restituisce un CodingKeyche sostituirà l'ultima chiave in questo array.
Ad esempio, UpperCamelCasechiavi JSON per lowerCamelCasenomi di proprietà:
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
}
}
}
Ora puoi codificare con la .convertToUpperCamelCasestrategia chiave:
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"}
e decodifica con la .convertFromUpperCamelCasestrategia chiave:
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; posso solo elencare l'unica chiave che sto cambiando?