Personalizar manualmente las claves de codificación
En su ejemplo, obtiene una conformidad generada automáticamente, Codable
ya que todas sus propiedades también cumplen Codable
. Esta conformidad crea automáticamente un tipo de clave que simplemente se corresponde con los nombres de las propiedades, que luego se usa para codificar / decodificar desde un único contenedor con clave.
Sin embargo, una muy buena característica de este conformidad autogenerado es que si se define un anidado enum
en su tipo llamado " CodingKeys
" (o usar una typealias
con este nombre) que se ajusta al CodingKey
protocolo - Swift utilizarán automáticamente este como el tipo de clave. Por lo tanto, esto le permite personalizar fácilmente las claves con las que se codifican / decodifican sus propiedades.
Entonces, lo que esto significa es que puedes decir:
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
}
}
Los nombres de los casos de enumeración deben coincidir con los nombres de las propiedades, y los valores sin procesar de estos casos deben coincidir con las claves que está codificando / decodificando (a menos que se especifique lo contrario, los valores sin procesar de una String
enumeración serán los mismos que los nombres de los casos ). Por lo tanto, la zip
propiedad ahora se codificará / decodificará con la clave "zip_code"
.
Las reglas exactas para la conformidad Encodable
/ autogenerado Decodable
se detallan en la propuesta de evolución (el énfasis es mío):
Además de la CodingKey
síntesis automática de requisitos para
enums
, Encodable
& los Decodable
requisitos también se pueden sintetizar automáticamente para ciertos tipos:
Los tipos que se ajustan a Encodable
cuyas propiedades son todas Encodable
obtienen un mapeo de propiedades de enumeración String
respaldado generado automáticamente CodingKey
a los nombres de caso. Similarmente para Decodable
tipos cuyas propiedades son todasDecodable
Los tipos que caen en (1) - y los tipos que proporcionan manualmente un CodingKey
enum
(con nombre CodingKeys
, directamente o mediante a typealias
) cuyos casos se asignan 1 a 1 a Encodable
/ Decodable
propiedades por nombre - obtienen una síntesis automática de init(from:)
y, encode(to:)
según corresponda, utilizando esas propiedades y claves
Los tipos que no pertenecen ni a (1) ni a (2) tendrán que proporcionar un tipo de clave personalizada si es necesario y proporcionar la suya propia init(from:)
y
encode(to:)
, según corresponda
Codificación de ejemplo:
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"}
Ejemplo de decodificación:
// 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
Claves JSON automáticas para camelCase
nombres de propiedades
En Swift 4.1, si cambia el nombre de su zip
propiedad a zipCode
, puede aprovechar las estrategias de codificación / decodificación de claves en JSONEncoder
y JSONDecoder
para convertir automáticamente las claves de codificación entre camelCase
y snake_case
.
Codificación de ejemplo:
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"}
Ejemplo de decodificación:
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")
Sin embargo, una cosa importante a tener en cuenta acerca de esta estrategia es que no podrá intercambiar algunos nombres de propiedades con acrónimos o iniciales que, de acuerdo con las pautas de diseño de la API de Swift , deben ser uniformemente en mayúsculas o minúsculas (dependiendo de la posición). ).
Por ejemplo, una propiedad nombrada someURL
se codificará con la clave some_url
, pero al decodificar, se transformará en someUrl
.
Para solucionar esto, tendrá que especificar manualmente la clave de codificación para que esa propiedad sea la cadena que espera el decodificador, por ejemplo, someUrl
en este caso (que aún será transformado some_url
por el codificador):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Esto no responde estrictamente a su pregunta específica, pero dada la naturaleza canónica de esta sesión de preguntas y respuestas, creo que vale la pena incluirla)
Asignación automática de claves JSON personalizada
En Swift 4.1, puede aprovechar las estrategias de codificación / decodificación de claves personalizadas en JSONEncoder
y JSONDecoder
, lo que le permite proporcionar una función personalizada para asignar claves de codificación.
La función que proporcionas toma a [CodingKey]
, que representa la ruta de codificación para el punto actual en la codificación / decodificación (en la mayoría de los casos, solo necesitarás considerar el último elemento, es decir, la clave actual). La función devuelve un CodingKey
que reemplazará la última clave de esta matriz.
Por ejemplo, UpperCamelCase
claves JSON para lowerCamelCase
nombres de propiedad:
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
}
}
}
Ahora puede codificar con la .convertToUpperCamelCase
estrategia clave:
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"}
y decodificar con la .convertFromUpperCamelCase
estrategia clave:
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
enumeración; ¿Puedo enumerar la clave que estoy cambiando?"""
es para un literal de varias líneas :)"
s: DCodable
contrario)Address
vincula innecesariamente a la decodificación de un objeto JSON que comienza en un lugar específico en el gráfico del objeto principal. Sería mucho más agradable abstraer la ruta de la clave de inicio hasta el decodificador mismo: aquí hay una implementación aproximada de hackey .Con Swift 4.2, de acuerdo con sus necesidades, puede utilizar una de las 3 estrategias siguientes para hacer que los nombres de propiedad personalizados de los objetos de su modelo coincidan con sus claves JSON.
# 1. Usar claves de codificación personalizadas
Cuando declara una estructura que se ajusta a
Codable
(Decodable
yEncodable
protocolos) con la siguiente implementación ...... el compilador genera automáticamente una enumeración anidada que se ajusta al
CodingKey
protocolo para usted.Por lo tanto, si las claves utilizadas en su formato de datos serializados no coinciden con los nombres de propiedad de su tipo de datos, puede implementar manualmente esta enumeración y establecer la apropiada
rawValue
para los casos requeridos.El siguiente ejemplo muestra cómo hacerlo:
Codificar (reemplazando la
zip
propiedad con la clave JSON "zip_code"):Decodificar (reemplazando la clave JSON "zip_code" con la
zip
propiedad):# 2. Uso de estrategias de codificación clave de caso de serpiente a caso de camello
Si su JSON tiene teclas de serpiente entubado y desea convertirlos a propiedades de camellos con carcasa para su modelo de objetos, puede configurar su
JSONEncoder
'skeyEncodingStrategy
yJSONDecoder
' skeyDecodingStrategy
propiedades a.convertToSnakeCase
.El siguiente ejemplo muestra cómo hacerlo:
Codificar (convertir propiedades en caja de camello en claves JSON en caja de serpiente):
Decodificar (convertir claves JSON en caja de serpiente en propiedades en caja de camello):
# 3. Uso de estrategias de codificación de claves personalizadas
Si es necesario,
JSONEncoder
y leJSONDecoder
permite establecer una estrategia personalizada para asignar claves de codificación usandoJSONEncoder.KeyEncodingStrategy.custom(_:)
yJSONDecoder.KeyDecodingStrategy.custom(_:)
.El siguiente ejemplo muestra cómo implementarlos:
Codificar (convertir las propiedades de la primera letra en minúsculas en claves JSON de la primera letra en mayúscula):
Decodificación (conversión de claves JSON de la primera letra en mayúscula en propiedades de la primera letra en minúscula):
Fuentes:
fuente
Lo que he hecho es crear una estructura propia como la que obtiene del JSON con respecto a sus tipos de datos.
Así:
Después de esto, debe crear una extensión de la misma
struct
extensióndecodable
yenum
de la misma estructura conCodingKey
y luego debe inicializar el decodificador usando esta enumeración con sus claves y tipos de datos (las claves vendrán de la enumeración y los tipos de datos vendrán o dirán referenciado desde la propia estructura)Debe cambiar aquí todas y cada una de las claves y tipos de datos de acuerdo con sus necesidades y usarlas con el decodificador.
fuente
Al usar CodingKey , puede usar claves personalizadas en un protocolo codificable o decodificable.
fuente