Personalização manual das chaves de codificação
Em seu exemplo, você está obtendo uma conformidade gerada automaticamente para, Codable
já que todas as suas propriedades também estão em conformidade Codable
. Essa conformidade cria automaticamente um tipo de chave que simplesmente corresponde aos nomes das propriedades - que é então usado para codificar / decodificar a partir de um único contêiner com chave.
No entanto, uma característica realmente interessante desta conformidade gerada automaticamente é que se você definir um aninhado enum
em seu tipo chamado " CodingKeys
" (ou usar um typealias
com este nome) que está em conformidade com oCodingKey
protocolo - o Swift usará isso automaticamente como o tipo de chave. Isso, portanto, permite que você personalize facilmente as chaves com as quais suas propriedades são codificadas / decodificadas.
Então, isso significa que você pode apenas dizer:
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
}
}
Os nomes dos casos enum precisam corresponder aos nomes das propriedades, e os valores brutos desses casos precisam corresponder às chaves para as quais você está codificando / decodificando (a menos que especificado de outra forma, os valores brutos de um String
enumeração serão iguais aos nomes de caso ) Portanto, a zip
propriedade agora será codificada / decodificada usando a chave "zip_code"
.
As regras exatas para a autogeração Encodable
/ Decodable
conformidade são detalhadas por na proposta de evolução (grifo meu):
Além da CodingKey
síntese automática de requisitos para
enums
, Encodable
e os Decodable
requisitos também podem ser sintetizados automaticamente para certos tipos:
Os tipos em conformidade com as Encodable
propriedades de cada Encodable
um String
obtêm CodingKey
propriedades de mapeamento enum geradas automaticamente para nomes de caso. Da mesma forma para Decodable
tipos cujas propriedades são todasDecodable
Tipos que se enquadram em (1) - e tipos que fornecem manualmente um CodingKey
enum
(nomeado CodingKeys
, diretamente ou via a typealias
) cujos casos mapeiam 1-para-1 para Encodable
/ Decodable
propriedades por nome - obtêm síntese automática deinit(from:)
e encode(to:)
conforme apropriado, usando essas propriedades e chaves
Tipos que não se enquadram em (1) nem (2) terão que fornecer um tipo de chave personalizado se necessário e fornecer o seu próprio init(from:)
e
encode(to:)
, conforme apropriado
Codificação de exemplo:
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"}
Exemplo de decodificação:
// 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
Chaves JSON automáticas para camelCase
nomes de propriedades
No Swift 4.1, se você renomear sua zip
propriedade para zipCode
, poderá aproveitar as vantagens das estratégias de codificação / decodificação de chave em JSONEncoder
e JSONDecoder
para converter automaticamente as chaves de codificação entre camelCase
e snake_case
.
Codificação de exemplo:
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"}
Exemplo de decodificação:
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")
Uma coisa importante a notar sobre esta estratégia, no entanto, é que ela não será capaz de percorrer alguns nomes de propriedade com acrônimos ou iniciais que, de acordo com as diretrizes de design da API Swift , devem ser uniformemente maiúsculas ou minúsculas (dependendo da posição )
Por exemplo, uma propriedade nomeada someURL
será codificada com a chave some_url
, mas na decodificação, será transformada em someUrl
.
Para corrigir isso, você terá que especificar manualmente a chave de codificação dessa propriedade para ser a string que o decodificador espera, por exemplo someUrl
, neste caso (que ainda será transformada some_url
pelo codificador):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Isso não responde estritamente à sua pergunta específica, mas dada a natureza canônica desta sessão de perguntas e respostas, acho que vale a pena incluí-la)
Mapeamento de teclas JSON automático personalizado
No Swift 4.1, você pode tirar proveito das estratégias de codificação / decodificação de chave personalizada em JSONEncoder
e JSONDecoder
, permitindo que você forneça uma função personalizada para mapear chaves de codificação.
A função que você fornece leva um [CodingKey]
, que representa o caminho de codificação para o ponto atual na codificação / decodificação (na maioria dos casos, você só precisa considerar o último elemento; ou seja, a chave atual). A função retorna um CodingKey
que substituirá a última chave nesta matriz.
Por exemplo, UpperCamelCase
chaves JSON para lowerCamelCase
nomes de propriedades:
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
}
}
}
Agora você pode codificar com a .convertToUpperCamelCase
estratégia principal:
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 decodifique com a .convertFromUpperCamelCase
estratégia principal:
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 apenas listar a chave que estou mudando?"""
é para um literal de várias linhas :)"
s: DCodable
contrário)Address
desnecessariamente vincule-se à decodificação de um objeto JSON que começa em um local específico no gráfico do objeto pai. Seria muito melhor abstrair o caminho da chave inicial até o próprio decodificador - aqui está uma implementação tosca de hackey .Com o Swift 4.2, de acordo com suas necessidades, você pode usar uma das 3 estratégias a seguir para fazer com que os nomes das propriedades personalizadas de seus objetos de modelo correspondam às suas chaves JSON.
# 1. Usando chaves de codificação personalizadas
Quando você declara uma estrutura que está em conformidade com
Codable
(Decodable
eEncodable
protocolos) com a implementação a seguir ...... o compilador gera automaticamente um enum aninhado que está em conformidade com o
CodingKey
protocolo para você.Portanto, se as chaves usadas em seu formato de dados serializados não corresponderem aos nomes de propriedade de seu tipo de dados, você pode implementar manualmente este enum e definir o apropriado
rawValue
para os casos necessários.O exemplo a seguir mostra como fazer:
Codificar (substituindo a
zip
propriedade pela chave JSON "zip_code"):Decodificar (substituindo a chave JSON "zip_code" pela
zip
propriedade):# 2. Usando a capa de cobra para estratégias de codificação de chave de capa de camelo
Se o seu JSON tem teclas encaixotado-cobra e quer convertê-los em propriedades encaixotado-camelo para o seu modelo de objeto, você pode definir o seu
JSONEncoder
'skeyEncodingStrategy
eJSONDecoder
' skeyDecodingStrategy
propriedades para.convertToSnakeCase
.O exemplo a seguir mostra como fazer:
Codificar (convertendo propriedades com caixa de camelo em chaves JSON com caixa de cobra):
Decodificar (converter chaves JSON com caixa de cobra em propriedades com caixa de camelo):
# 3. Usando estratégias de codificação de chaves personalizadas
Se necessário,
JSONEncoder
eJSONDecoder
permitir que você defina uma estratégia personalizada para mapear as chaves de codificação usandoJSONEncoder.KeyEncodingStrategy.custom(_:)
eJSONDecoder.KeyDecodingStrategy.custom(_:)
.O exemplo a seguir mostra como implementá-los:
Codificar (convertendo as propriedades da primeira letra minúscula em chaves JSON da primeira letra maiúscula):
Decodificar (convertendo as chaves JSON da primeira letra maiúscula em propriedades da primeira letra minúscula):
Fontes:
fonte
O que fiz é criar a própria estrutura, exatamente como o que você está obtendo do JSON com relação aos seus tipos de dados.
Bem assim:
Depois disso você precisa criar uma extensão da mesma
struct
extensãodecodable
eenum
da mesma estrutura comCodingKey
e então você precisa inicializar o decodificador usando este enum com suas chaves e tipos de dados (as chaves virão do enum e os tipos de dados virão ou dirão referenciado da própria estrutura)Você precisa alterar aqui cada chave e tipo de dados de acordo com suas necessidades e usá-los com o decodificador.
fonte
Ao usar CodingKey, você pode usar chaves personalizadas em protocolo codificável ou decodificável.
fonte