Manuelles Anpassen der Codierungsschlüssel
In Ihrem Beispiel erhalten Sie eine automatisch generierte Konformität mit, Codable
da 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 enum
in Ihrem Typ „bezeichnet CodingKeys
“ (oder eine Verwendung typealias
mit diesem Namen) , dass entspricht die CodingKey
Protokoll - 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 String
Aufzählung den Fallnamen ). Daher wird die zip
Eigenschaft jetzt mit dem Schlüssel codiert / decodiert "zip_code"
.
Die genauen Regeln für die automatische Generierung Encodable
/ Decodable
Konformität sind im Evolutionsvorschlag (Schwerpunkt Mine) aufgeführt:
Zusätzlich zur automatischen CodingKey
Anforderungssynthese für
enums
können Encodable
& Decodable
Anforderungen auch für bestimmte Typen automatisch synthetisiert werden:
Typen, Encodable
deren Eigenschaften alle entsprechen, Encodable
erhalten automatisch generierte Enum-Mapping-Eigenschaften für String
Fallnamen CodingKey
. Ähnliches gilt für Decodable
Typen, 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
/ Decodable
Eigenschaften 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_case
JSON-Schlüssel für camelCase
Eigenschaftsnamen
Wenn Sie in Swift 4.1 Ihre zip
Eigenschaft in umbenennen zipCode
, können Sie die Schlüsselcodierungs- / Decodierungsstrategien für JSONEncoder
und nutzen JSONDecoder
, um Codierungsschlüssel zwischen camelCase
und 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 someURL
mit 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. someUrl
in diesem Fall (der some_url
vom 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 JSONEncoder
und nutzen JSONDecoder
und 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: UpperCamelCase
JSON-Schlüssel für lowerCamelCase
Eigenschaftsnamen:
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 .convertToUpperCamelCase
Schlü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 .convertFromUpperCamelCase
Schlü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")
CodingKeys
Aufzählung; Kann ich nur den einen Schlüssel auflisten, den ich ändere?"""
ist für ein mehrzeiliges Literal :)"
s: DCodable
anderes halten)Address
unnötig dazu führt, dass ein JSON-Objekt dekodiert wird, das an einer bestimmten Stelle im übergeordneten Objektdiagramm beginnt. Es wäre viel schöner, den Startschlüsselpfad bis zum Decoder selbst zu abstrahieren - hier ist eine grobe hackey-artige Implementierung .Mit Swift 4.2 können Sie je nach Bedarf eine der drei folgenden Strategien verwenden, damit die benutzerdefinierten Eigenschaftsnamen Ihrer Modellobjekte mit Ihren JSON-Schlüsseln übereinstimmen.
# 1. Verwenden von benutzerdefinierten Codierungsschlüsseln
Wenn Sie eine Struktur deklarieren, die der folgenden Implementierung entspricht
Codable
(Decodable
und dieseEncodable
protokolliert) ...... generiert der Compiler automatisch eine verschachtelte Aufzählung, die dem
CodingKey
Protokoll für Sie entspricht.Wenn die in Ihrem serialisierten Datenformat verwendeten Schlüssel nicht mit den Eigenschaftsnamen Ihres Datentyps übereinstimmen, können Sie diese Aufzählung manuell implementieren und die
rawValue
für die erforderlichen Fälle geeignete festlegen .Das folgende Beispiel zeigt, wie es geht:
Encode (Ersetzen der
zip
Eigenschaft durch den JSON-Schlüssel "Postleitzahl"):Dekodieren (JSON-Schlüssel "Postleitzahl" durch
zip
Eigenschaft ersetzen ):# 2. Verwenden von Snake Case zum Kamelfall Schlüsselcodierungsstrategien
Wenn Ihr JSON schlangen verrohrten Schlüssel hat und Sie wollen , dass sie Kamel Gefasste Eigenschaften für Ihr Modell Objekt konvertieren, können Sie Ihre Set
JSONEncoder
‚skeyEncodingStrategy
undJSONDecoder
‘ skeyDecodingStrategy
Eigenschaften.convertToSnakeCase
.Das folgende Beispiel zeigt, wie es geht:
Encode (Konvertieren von Eigenschaften in Kamelhülle in JSON-Schlüssel mit Schlangenhülle):
Dekodieren (Konvertieren von JSON-Schlüsseln mit Schlangengehäuse in Eigenschaften mit Kamelgehäuse):
#3. Verwenden benutzerdefinierter Schlüsselcodierungsstrategien
Falls erforderlich,
JSONEncoder
undJSONDecoder
ermöglicht es Ihnen , eine benutzerdefinierte Strategie setzen Kodierschlüssel zur Karte mitJSONEncoder.KeyEncodingStrategy.custom(_:)
undJSONDecoder.KeyDecodingStrategy.custom(_:)
.Das folgende Beispiel zeigt, wie sie implementiert werden:
Codieren (Konvertieren von Eigenschaften für Kleinbuchstaben in Großbuchstaben in JSON-Schlüssel für Großbuchstaben):
Dekodieren (Konvertieren von JSON-Schlüsseln für Großbuchstaben in Kleinbuchstaben in Kleinbuchstaben):
Quellen:
quelle
Was ich getan habe, ist eine eigene Struktur zu erstellen, genau wie Sie es von JSON in Bezug auf seine Datentypen erhalten.
Genau wie dieser:
Danach müssen Sie eine Erweiterung derselben
struct
Erweiterungdecodable
undenum
derselben Struktur mit erstellenCodingKey
und dann den Decoder mithilfe dieser Aufzählung mit seinen Schlüsseln und Datentypen initialisieren (Schlüssel kommen aus der Aufzählung und die Datentypen kommen oder sagen aus der Struktur selbst referenziert)Sie müssen hier jeden Schlüssel und jeden Datentyp entsprechend Ihren Anforderungen ändern und mit dem Decoder verwenden.
quelle
Mit CodingKey können Sie benutzerdefinierte Schlüssel in codierbaren oder decodierbaren Protokollen verwenden.
quelle