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?"""
est pour un littéral multiligne :)"
s: DCodable
contraire)Address
vous lie inutilement au décodage d'un objet JSON qui commence à un endroit spécifique dans le graphe de l'objet parent. Il serait beaucoup plus agréable d'abstraire le chemin de la clé de départ jusqu'au décodeur lui-même - voici une implémentation approximative de hackey-ish .Avec Swift 4.2, selon vos besoins, vous pouvez utiliser l'une des 3 stratégies suivantes afin de faire correspondre les noms de propriétés personnalisées de vos objets modèles à vos clés JSON.
#1. Utilisation de clés de codage personnalisées
Lorsque vous déclarez une structure conforme à
Codable
(Decodable
et auxEncodable
protocoles) avec l'implémentation suivante ...... le compilateur génère automatiquement une énumération imbriquée conforme au
CodingKey
protocole pour vous.Par conséquent, si les clés utilisées dans votre format de données sérialisées ne correspondent pas aux noms de propriété de votre type de données, vous pouvez implémenter manuellement cette énumération et définir la valeur appropriée
rawValue
pour les cas requis.L'exemple suivant montre comment faire:
Encode (en remplaçant la
zip
propriété par la clé JSON "zip_code"):Décoder (en remplaçant la clé JSON "zip_code" par la
zip
propriété):# 2. Utilisation des stratégies de codage de clé de cas de serpent à cas de chameau
Si votre JSON a des touches de serpent-tubé et que vous voulez les convertir en propriétés en CamelCase pour votre modèle objet, vous pouvez définir votre
JSONEncoder
« skeyEncodingStrategy
etJSONDecoder
» dekeyDecodingStrategy
propriétés à.convertToSnakeCase
.L'exemple suivant montre comment faire:
Encode (conversion des propriétés camel cased en clés JSON snake cased):
Decode (conversion des clés JSON casse snake en propriétés camel cased):
# 3. Utilisation de stratégies de codage de clé personnalisées
Si nécessaire,
JSONEncoder
etJSONDecoder
vous permet de définir une stratégie personnalisée pour mapper les clés de codage à l'aide deJSONEncoder.KeyEncodingStrategy.custom(_:)
etJSONDecoder.KeyDecodingStrategy.custom(_:)
.L'exemple suivant montre comment les implémenter:
Encode (conversion des propriétés de première lettre minuscules en clés JSON de première lettre majuscule):
Decode (conversion des clés JSON de première lettre majuscules en propriétés de première lettre minuscules):
Sources:
la source
Ce que j'ai fait, c'est créer ma propre structure, tout comme ce que vous obtenez du JSON en ce qui concerne ses types de données.
Juste comme ça:
Après cela, vous devez créer une extension de la même
struct
extensiondecodable
etenum
de la même structure avecCodingKey
, puis vous devez initialiser le décodeur en utilisant cette énumération avec ses clés et ses types de données (les clés proviendront de l'énumération et les types de données viendront ou diront référencé à partir de la structure elle-même)Vous devez modifier ici chaque clé et chaque type de données en fonction de vos besoins et l'utiliser avec le décodeur.
la source
En utilisant CodingKey vous pouvez utiliser les touches personnalisées dans le protocole codable ou décodable.
la source