Personalizar manualmente las claves de codificación
En su ejemplo, obtiene una conformidad generada automáticamente, Codableya 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 enumen su tipo llamado " CodingKeys" (o usar una typealiascon este nombre) que se ajusta al CodingKeyprotocolo - 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 Stringenumeración serán los mismos que los nombres de los casos ). Por lo tanto, la zippropiedad ahora se codificará / decodificará con la clave "zip_code".
Las reglas exactas para la conformidad Encodable/ autogenerado Decodablese detallan en la propuesta de evolución (el énfasis es mío):
Además de la CodingKeysíntesis automática de requisitos para
enums, Encodable& los Decodablerequisitos también se pueden sintetizar automáticamente para ciertos tipos:
Los tipos que se ajustan a Encodablecuyas propiedades son todas Encodableobtienen un mapeo de propiedades de enumeración Stringrespaldado generado automáticamente CodingKeya los nombres de caso. Similarmente para Decodabletipos 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/ Decodablepropiedades 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_caseClaves JSON automáticas para camelCasenombres de propiedades
En Swift 4.1, si cambia el nombre de su zippropiedad a zipCode, puede aprovechar las estrategias de codificación / decodificación de claves en JSONEncodery JSONDecoderpara convertir automáticamente las claves de codificación entre camelCasey 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 someURLse 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, someUrlen este caso (que aún será transformado some_urlpor 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 JSONEncodery 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 CodingKeyque reemplazará la última clave de esta matriz.
Por ejemplo, UpperCamelCaseclaves JSON para lowerCamelCasenombres 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 .convertToUpperCamelCaseestrategia 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 .convertFromUpperCamelCaseestrategia 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")
CodingKeysenumeración; ¿Puedo enumerar la clave que estoy cambiando?"""es para un literal de varias líneas :)"s: DCodablecontrario)Addressvincula 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(DecodableyEncodableprotocolos) con la siguiente implementación ...... el compilador genera automáticamente una enumeración anidada que se ajusta al
CodingKeyprotocolo 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
rawValuepara los casos requeridos.El siguiente ejemplo muestra cómo hacerlo:
Codificar (reemplazando la
zippropiedad con la clave JSON "zip_code"):Decodificar (reemplazando la clave JSON "zip_code" con la
zippropiedad):# 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'skeyEncodingStrategyyJSONDecoder' skeyDecodingStrategypropiedades 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,
JSONEncodery leJSONDecoderpermite 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
structextensióndecodableyenumde la misma estructura conCodingKeyy 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