Manuelles Anpassen der Codierungsschlüssel
In Ihrem Beispiel erhalten Sie eine automatisch generierte Konformität mit, Codableda auch alle Ihre Eigenschaften übereinstimmen Codable. Diese Konformität erstellt automatisch einen Schlüsseltyp, der einfach den Eigenschaftsnamen entspricht. Dieser wird dann verwendet, um aus einem einzelnen Schlüsselcontainer zu codieren / daraus zu decodieren.
Doch ein wirklich nettes Feature dieser automatisch generierten Übereinstimmung besteht darin , dass , wenn Sie definieren eine verschachtelte enumin Ihrem Typ „bezeichnet CodingKeys“ (oder eine Verwendung typealiasmit diesem Namen) , dass entspricht die CodingKeyProtokoll - Swift wird automatisch verwenden diese als Schlüsseltyp. Auf diese Weise können Sie die Schlüssel, mit denen Ihre Eigenschaften codiert / decodiert werden, einfach anpassen.
Das heißt also, Sie können einfach sagen:
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
}
}
Die Enum-Fallnamen müssen mit den Eigenschaftsnamen übereinstimmen, und die Rohwerte dieser Fälle müssen mit den Schlüsseln übereinstimmen, in die Sie codieren / von denen Sie decodieren (sofern nicht anders angegeben, entsprechen die Rohwerte einer StringAufzählung den Fallnamen ). Daher wird die zipEigenschaft jetzt mit dem Schlüssel codiert / decodiert "zip_code".
Die genauen Regeln für die automatische Generierung Encodable/ DecodableKonformität sind im Evolutionsvorschlag (Schwerpunkt Mine) aufgeführt:
Zusätzlich zur automatischen CodingKeyAnforderungssynthese für
enumskönnen Encodable& DecodableAnforderungen auch für bestimmte Typen automatisch synthetisiert werden:
Typen, Encodablederen Eigenschaften alle entsprechen, Encodableerhalten automatisch generierte Enum-Mapping-Eigenschaften für StringFallnamen CodingKey. Ähnliches gilt für DecodableTypen, deren Eigenschaften alle sindDecodable
Typen, die in (1) fallen - und Typen, die manuell einen CodingKey enum(benannten CodingKeys, direkten oder über a typealias) bereitstellen , dessen Fälle 1-zu-1 Encodable/ DecodableEigenschaften nach Namen zuordnen - erhalten eine automatische Synthese von init(from:)und encode(to:)gegebenenfalls unter Verwendung dieser Eigenschaften und Schlüssel
Typen , die in keiner fallen (1) noch (2) wird eine benutzerdefinierte Schlüsseltyp zur Verfügung stellen müssen , falls erforderlich und stellen ihre eigenen init(from:)und
encode(to:), gegebenenfalls
Beispielcodierung:
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"}
Beispieldecodierung:
// 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")
Automatische snake_caseJSON-Schlüssel für camelCaseEigenschaftsnamen
Wenn Sie in Swift 4.1 Ihre zipEigenschaft in umbenennen zipCode, können Sie die Schlüsselcodierungs- / Decodierungsstrategien für JSONEncoderund nutzen JSONDecoder, um Codierungsschlüssel zwischen camelCaseund automatisch zu konvertieren snake_case.
Beispielcodierung:
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"}
Beispieldecodierung:
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")
Eine wichtige Anmerkung zu dieser Strategie ist jedoch, dass einige Eigenschaftsnamen nicht mit Akronymen oder Initialismen umgangen werden können, die gemäß den Swift API-Entwurfsrichtlinien (je nach Position) einheitlich in Groß- oder Kleinschreibung geschrieben werden sollten ).
Beispielsweise wird eine Eigenschaft mit dem Namen someURLmit dem Schlüssel codiert some_url, aber beim Decodieren wird diese in transformiert someUrl.
Um dies zu beheben, müssen Sie den Codierungsschlüssel für diese Eigenschaft manuell als Zeichenfolge angeben, die der Decoder erwartet, z. B. someUrlin diesem Fall (der some_urlvom Encoder weiterhin transformiert wird):
struct S : Codable {
private enum CodingKeys : String, CodingKey {
case someURL = "someUrl", someOtherProperty
}
var someURL: String
var someOtherProperty: String
}
(Dies beantwortet Ihre spezifische Frage nicht unbedingt, aber angesichts des kanonischen Charakters dieser Fragen und Antworten halte ich es für sinnvoll, sie einzubeziehen.)
Benutzerdefinierte automatische JSON-Schlüsselzuordnung
In Swift 4.1 können Sie die benutzerdefinierten Schlüsselcodierungs- / Decodierungsstrategien für JSONEncoderund nutzen JSONDecoderund eine benutzerdefinierte Funktion zum Zuordnen von Codierungsschlüsseln bereitstellen.
Die von Ihnen bereitgestellte Funktion verwendet a [CodingKey], das den Codierungspfad für den aktuellen Punkt beim Codieren / Decodieren darstellt (in den meisten Fällen müssen Sie nur das letzte Element berücksichtigen, dh den aktuellen Schlüssel). Die Funktion gibt a zurück CodingKey, das den letzten Schlüssel in diesem Array ersetzt.
Beispiel: UpperCamelCaseJSON-Schlüssel für lowerCamelCaseEigenschaftsnamen:
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
}
}
}
Sie können jetzt mit der .convertToUpperCamelCaseSchlüsselstrategie codieren :
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"}
und mit der .convertFromUpperCamelCaseSchlüsselstrategie dekodieren :
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")
CodingKeysAufzählung; Kann ich nur den einen Schlüssel auflisten, den ich ändere?