Personalizzazione manuale delle chiavi di codifica
Nel tuo esempio, stai ottenendo una conformità generata automaticamente a Codable
come 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 enum
nel vostro tipo chiamato " CodingKeys
" (o utilizzare un typealias
con questo nome) conforme al CodingKey
protocollo - 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 String
un'enumerazione saranno gli stessi dei nomi dei casi ). Pertanto, la zip
proprietà verrà ora codificata / decodificata utilizzando la chiave "zip_code"
.
Le regole esatte per l'auto-generazione Encodable
/ Decodable
conformità sono dettagliate dalla proposta di evoluzione (enfasi mia):
Oltre ad automatico CodingKey
sintesi requisito
enums
, Encodable
e Decodable
requisiti possono essere sintetizzati automaticamente per determinati tipi come bene:
Tipi conformi alla Encodable
cui proprietà sono tutti Encodable
ottenere un generate automaticamente String
emessi a fronte di CodingKey
proprietà di mappatura enum ai nomi di casi. Allo stesso modo per i Decodable
tipi 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
/ Decodable
proprietà 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_case
Chiavi JSON automatiche per camelCase
i nomi delle proprietà
In Swift 4.1, se rinomini la tua zip
proprietà in zipCode
, puoi sfruttare le strategie di codifica / decodifica delle chiavi su JSONEncoder
e JSONDecoder
per convertire automaticamente le chiavi di codifica tra camelCase
e 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 someURL
verrà 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 someUrl
in questo caso (che verrà comunque trasformata some_url
dal 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 JSONEncoder
e 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 CodingKey
che sostituirà l'ultima chiave in questo array.
Ad esempio, UpperCamelCase
chiavi JSON per lowerCamelCase
nomi 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 .convertToUpperCamelCase
strategia 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 .convertFromUpperCamelCase
strategia 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")
CodingKeys
enum; posso solo elencare l'unica chiave che sto cambiando?