Налаштування ключів кодування вручну
У вашому прикладі ви отримуєте автоматично створену відповідність тому, Codable
як відповідають і всі ваші властивості Codable
. Ця відповідність автоматично створює тип ключа, який просто відповідає іменам властивостей - який потім використовується для кодування та декодування з одного контейнера з ключем.
Однак одна з дійсно акуратних властивостей цього автоматично сформованого відповідності полягає в тому, що якщо ви визначите вкладений enum
у своєму типі під назвою " CodingKeys
" (або використовуєте a 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
захищені переліку переліку CodingKey
властивості перерахування до імен справ. Аналогічно для Decodable
типів, властивості яких усіDecodable
Типи, що потрапляють у (1) - та типи, які надають вручну CodingKey
enum
(названі CodingKeys
, безпосередньо чи через a 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
, що дозволяють вам надати спеціальну функцію для відображення ключів кодування.
Функція, яку ви надаєте, приймає a [CodingKey]
, який представляє шлях кодування для поточної точки кодування / декодування (у більшості випадків вам потрібно буде враховувати лише останній елемент; тобто поточний ключ). Функція повертає a, 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
перерахунком; чи можу я просто перерахувати один ключ, який я змінюю?