Personnalisation manuelle des clés de codage
Dans votre exemple, vous obtenez une conformité générée automatiquement à Codable
car toutes vos propriétés sont également conformes à Codable
. Cette conformité crée automatiquement un type de clé qui correspond simplement aux noms de propriété - qui est ensuite utilisé pour encoder / décoder à partir d'un seul conteneur à clé.
Cependant , une très caractéristique propre de cette conformité est généré automatiquement que si vous définissez un imbriqué enum
dans votre type appelé « CodingKeys
» (ou utiliser un typealias
avec ce nom) qui est conforme au CodingKey
protocole - Swift utilisera automatiquement ce que le type de clé. Cela vous permet donc de personnaliser facilement les clés avec lesquelles vos propriétés sont encodées / décodées.
Donc, ce que cela signifie, c'est que vous pouvez simplement 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
}
}
Les noms de cas d'énumération doivent correspondre aux noms de propriété, et les valeurs brutes de ces cas doivent correspondre aux clés avec lesquelles vous encodez / décodez (sauf indication contraire, les valeurs brutes d'une String
énumération seront les mêmes que les noms de cas ). Par conséquent, la zip
propriété sera désormais encodée / décodée à l'aide de la clé "zip_code"
.
Les règles exactes de l'auto-généré Encodable
/ Decodable
conformité sont détaillées par la proposition d'évolution (c'est moi qui souligne):
En plus de la CodingKey
synthèse automatique des exigences pour
enums
, Encodable
& les Decodable
exigences peuvent être automatiquement synthétisées pour certains types:
Les types conformes Encodable
dont les propriétés sont toutes Encodable
obtiennent une énumération enum générée automatiquement String
qui CodingKey
mappe les propriétés aux noms de cas. De même pour les Decodable
types dont les propriétés sont toutesDecodable
Les types tombant dans (1) - et les types qui fournissent manuellement un CodingKey
enum
(named CodingKeys
, directement ou via a typealias
) dont les cas mappent 1-to-1 à Encodable
/ Decodable
properties by name - obtiennent une synthèse automatique de init(from:)
et, encode(to:)
le cas échéant, en utilisant ces propriétés et clés
Les types qui ne relèvent ni de (1) ni (2) devront fournir un type de clé personnalisé si nécessaire et fournir le leur init(from:)
et
encode(to:)
, le cas échéant
Exemple d'encodage:
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"}
Exemple de décodage:
// 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
Clés JSON automatiques pour camelCase
les noms de propriétés
Dans Swift 4.1, si vous renommez votre zip
propriété en zipCode
, vous pouvez profiter des stratégies d'encodage / décodage de clé sur JSONEncoder
et JSONDecoder
afin de convertir automatiquement les clés de codage entre camelCase
et snake_case
.
Exemple d'encodage:
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"}
Exemple de décodage:
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")
Cependant, une chose importante à noter à propos de cette stratégie est qu'elle ne pourra pas aller-retour pour certains noms de propriétés avec des acronymes ou des initialismes qui, selon les directives de conception de l'API Swift , doivent être uniformément en majuscules ou en minuscules (selon la position ).
Par exemple, une propriété nommée someURL
sera encodée avec la clé some_url
, mais lors du décodage, elle sera transformée en someUrl
.
Pour résoudre ce problème, vous devrez spécifier manuellement la clé de codage pour cette propriété comme étant la chaîne attendue par le décodeur, par exemple someUrl
dans ce cas (qui sera toujours transformée some_url
par l'encodeur):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Cela ne répond pas strictement à votre question spécifique, mais étant donné la nature canonique de ce Q&A, je pense que cela vaut la peine d'être inclus)
Mappage de clé JSON automatique personnalisé
Dans Swift 4.1, vous pouvez tirer parti des stratégies d'encodage / décodage de touches personnalisées sur JSONEncoder
et JSONDecoder
, vous permettant de fournir une fonction personnalisée pour mapper les clés de codage.
La fonction que vous fournissez prend a [CodingKey]
, qui représente le chemin de codage pour le point actuel dans le codage / décodage (dans la plupart des cas, vous n'aurez besoin de considérer que le dernier élément; c'est-à-dire la clé actuelle). La fonction renvoie un CodingKey
qui remplacera la dernière clé de ce tableau.
Par exemple, UpperCamelCase
les clés JSON pour lowerCamelCase
les noms de propriété:
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
}
}
}
Vous pouvez maintenant encoder avec la .convertToUpperCamelCase
stratégie clé:
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"}
et décoder avec la .convertFromUpperCamelCase
stratégie clé:
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
énumération; puis-je simplement lister la clé que je change?