Настройка ключей кодирования вручную
В вашем примере вы получаете автоматически сгенерированное соответствие, Codable
которому также соответствуют все ваши свойства Codable
. Это соответствие автоматически создает тип ключа, который просто соответствует именам свойств, который затем используется для кодирования / декодирования из контейнера с одним ключом.
Однако одна действительно интересная особенность этого автоматически сгенерированного соответствия заключается в том, что если вы определяете вложенный enum
в свой тип с именем " CodingKeys
" (или используете typealias
с этим именем), который соответствует CodingKey
протоколу, Swift автоматически будет использовать его в качестве типа ключа. Таким образом, это позволяет вам легко настраивать ключи, с помощью которых ваши свойства кодируются / декодируются.
Это означает, что вы можете просто сказать:
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
}
}
Имена кейсов перечисления должны соответствовать именам свойств, а необработанные значения этих кейсов должны соответствовать ключам, которые вы кодируете / декодируете (если не указано иное, необработанные значения String
перечисления будут такими же, как имена кейсов. ). Следовательно, zip
свойство теперь будет кодироваться / декодироваться с помощью ключа "zip_code"
.
Точные правила для автогенерации Encodable
/ Decodable
соответствия детализированы в предложении эволюции (выделено мной):
В дополнение к автоматическому CodingKey
синтезу Требование
enums
, Encodable
и Decodable
требования могут быть автоматически синтезированы для определенных типов , а также:
Типы , соответствующие Encodable
свойства которого все Encodable
получает автоматически генерируемый String
-backed CodingKey
перечислений свойства отображения имен в случае. Аналогично для Decodable
типов, все свойства которыхDecodable
Типы попадающих в (1) - и типах , которые вручную обеспечивают CodingKey
enum
( по имени CodingKeys
, непосредственно, или через typealias
) , чьи дела на карту 1-к-1 к Encodable
/ Decodable
свойств по названию - получить автоматический синтез init(from:)
и по encode(to:)
мере необходимости, используя эти свойства и ключи
Типы, которые не попадают ни в (1), ни в (2), должны будут предоставлять настраиваемый тип ключа, если необходимо, и предоставлять свои собственные init(from:)
и
encode(to:)
, при необходимости,
Пример кодировки:
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"}
Пример расшифровки:
// 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
ключи JSON для camelCase
имен свойств
В Swift 4.1, если вы переименуете свое zip
свойство в zipCode
, вы можете воспользоваться преимуществами ключевых стратегий кодирования / декодирования JSONEncoder
и JSONDecoder
для автоматического преобразования ключей кодирования между camelCase
и snake_case
.
Пример кодировки:
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"}
Пример расшифровки:
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")
Тем не менее, одна важная вещь, которую следует отметить в этой стратегии, заключается в том, что она не сможет передавать некоторые имена свойств с помощью акронимов или инициализмов, которые, согласно руководящим принципам проектирования Swift API , должны быть одинаково прописными или строчными (в зависимости от положения ).
Например, свойство с именем someURL
будет закодировано с помощью ключа some_url
, но при декодировании оно будет преобразовано в someUrl
.
Чтобы исправить это, вам нужно вручную указать ключ кодирования для этого свойства, который должен быть строкой, которую ожидает декодер, например, someUrl
в этом случае (которая все равно будет преобразована some_url
кодировщиком):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Это не совсем ответ на ваш конкретный вопрос, но, учитывая канонический характер этого вопроса и ответа, я считаю, что его стоит включить)
Пользовательское автоматическое сопоставление ключей JSON
В Swift 4.1 вы можете воспользоваться преимуществами настраиваемых стратегий кодирования / декодирования ключей на JSONEncoder
и JSONDecoder
, что позволяет вам предоставить настраиваемую функцию для сопоставления ключей кодирования.
Предоставляемая вами функция принимает значение [CodingKey]
, которое представляет путь кодирования для текущей точки кодирования / декодирования (в большинстве случаев вам нужно будет учитывать только последний элемент, то есть текущий ключ). Функция возвращает CodingKey
, который заменит последний ключ в этом массиве.
Например, UpperCamelCase
ключи JSON для lowerCamelCase
имен свойств:
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
}
}
}
Теперь вы можете кодировать с помощью .convertToUpperCamelCase
ключевой стратегии:
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"}
и декодируем с помощью .convertFromUpperCamelCase
ключевой стратегии:
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; могу я просто перечислить один ключ, который я меняю?"""
для многострочного литерала :)"
s: DCodable
другому)Address
излишне связывает вас с декодированием объекта JSON, который начинается в определенном месте в графе родительских объектов. Было бы намного лучше абстрагироваться от начального пути ключа до самого декодера - вот грубая хакерская реализация .В Swift 4.2, в соответствии с вашими потребностями, вы можете использовать одну из трех следующих стратегий, чтобы имена настраиваемых свойств ваших объектов модели соответствовали вашим JSON-ключам.
№1. Использование пользовательских ключей кодирования
Когда вы объявляете структуру, которая соответствует
Codable
(Decodable
иEncodable
протоколам) со следующей реализацией ...... компилятор автоматически генерирует вложенное перечисление, которое соответствует
CodingKey
протоколу для вас.Следовательно, если ключи, используемые в вашем формате сериализованных данных, не соответствуют именам свойств из вашего типа данных, вы можете вручную реализовать это перечисление и установить подходящие
rawValue
для требуемых случаев.В следующем примере показано, как это сделать:
Кодировать (заменяя
zip
свойство на ключ JSON "zip_code"):Декодирование (замена ключа JSON "zip_code" на
zip
свойство):№2. Стратегии кодирования ключей от змеиного футляра в верблюжий футляр
Если JSON имеет змеиные обсаженных ключи , и вы хотите , чтобы преобразовать их в верблюжьих-обсаженных свойства для вашей модели объекта, вы можете установить
JSONEncoder
«SkeyEncodingStrategy
иJSONDecoder
» skeyDecodingStrategy
свойства.convertToSnakeCase
.В следующем примере показано, как это сделать:
Кодировать (преобразование свойств с верблюжьей оболочкой в ключи JSON со змеиным корпусом):
Декодирование (преобразование ключей JSON с оболочкой змеи в свойства с оболочкой верблюда):
№3. Использование пользовательских стратегий кодирования ключей
Если необходимо,
JSONEncoder
иJSONDecoder
вы можете установить собственную стратегию для сопоставления ключей кодирования с помощьюJSONEncoder.KeyEncodingStrategy.custom(_:)
иJSONDecoder.KeyDecodingStrategy.custom(_:)
.В следующем примере показано, как их реализовать:
Кодировать (преобразование свойств первой буквы в нижнем регистре в ключи JSON с первой буквой в верхнем регистре):
Декодирование (преобразование ключей JSON с первой буквой в верхнем регистре в свойства первой буквы в нижнем регистре):
Источники:
источник
Я создал собственную структуру, аналогичную той, которую вы получаете от JSON, в отношении его типов данных.
Именно так:
После этого вам нужно создать расширение того же
struct
расширенияdecodable
и тойenum
же структуры с,CodingKey
а затем вам нужно инициализировать декодер, используя это перечисление с его ключами и типами данных (ключи будут поступать из перечисления, а типы данных будут приходить или говорить ссылка на саму структуру)Здесь вам нужно изменить каждый ключ и типы данных в соответствии с вашими потребностями и использовать их с декодером.
источник
Используя CodingKey, вы можете использовать собственные ключи в кодируемом или декодируемом протоколе.
источник