Swift 4 hat das neue Codable
Protokoll hinzugefügt . Wenn ich es verwende JSONDecoder
, müssen anscheinend alle nicht optionalen Eigenschaften meiner Codable
Klasse Schlüssel im JSON haben, oder es wird ein Fehler ausgegeben.
Jede Eigenschaft meiner Klasse optional zu machen, scheint ein unnötiger Aufwand zu sein, da ich wirklich den Wert im json oder einen Standardwert verwenden möchte. (Ich möchte nicht, dass die Eigenschaft Null ist.)
Gibt es eine Möglichkeit, dies zu tun?
class MyCodable: Codable {
var name: String = "Default Appleseed"
}
func load(input: String) {
do {
if let data = input.data(using: .utf8) {
let result = try JSONDecoder().decode(MyCodable.self, from: data)
print("name: \(result.name)")
}
} catch {
print("error: \(error)")
// `Error message: "Key not found when expecting non-optional type
// String for coding key \"name\""`
}
}
let goodInput = "{\"name\": \"Jonny Appleseed\" }"
let badInput = "{}"
load(input: goodInput) // works, `name` is Jonny Applessed
load(input: badInput) // breaks, `name` required since property is non-optional
Antworten:
Ein Ansatz, den ich bevorzuge, ist die Verwendung von sogenannten DTOs - Datenübertragungsobjekt. Es ist eine Struktur, die Codable entspricht und das gewünschte Objekt darstellt.
Dann initiieren Sie einfach das Objekt, das Sie in der App mit diesem DTO verwenden möchten.
Dieser Ansatz ist auch gut, da Sie das endgültige Objekt nach Belieben umbenennen und ändern können. Es ist klar und erfordert weniger Code als die manuelle Dekodierung. Darüber hinaus können Sie mit diesem Ansatz die Netzwerkschicht von anderen Apps trennen.
quelle
Sie können die
init(from decoder: Decoder)
Methode in Ihrem Typ implementieren, anstatt die Standardimplementierung zu verwenden:Sie können auch
name
eine konstante Eigenschaft erstellen (wenn Sie möchten):oder
Zu Ihrem Kommentar: Mit einer benutzerdefinierten Erweiterung
Sie können die init-Methode als implementieren
aber das ist nicht viel kürzer als
quelle
CodingKeys
Aufzählung verwenden können (um die benutzerdefinierte Definition zu entfernen) :)ObjectMapper
handhabt dies sehr gut.Decodable
und auch Ihre eigene Implementierung von bereitstelleninit(from:)
? In diesem Fall geht der Compiler davon aus, dass Sie die Dekodierung manuell durchführen möchten, und synthetisiert daher keineCodingKeys
Aufzählung für Sie. Wie Sie sagen,Codable
funktioniert das Anpassen an stattdessen, weil der Compiler jetztencode(to:)
für Sie synthetisiert und so auch synthetisiertCodingKeys
. Wenn Sie auch Ihre eigene Implementierung von bereitstellenencode(to:)
,CodingKeys
wird diese nicht mehr synthetisiert.Eine Lösung wäre die Verwendung einer berechneten Eigenschaft, die standardmäßig den gewünschten Wert verwendet, wenn der JSON-Schlüssel nicht gefunden wird. Dies fügt zusätzliche Ausführlichkeit hinzu, da Sie eine andere Eigenschaft deklarieren müssen und die
CodingKeys
Aufzählung hinzufügen müssen (falls nicht bereits vorhanden). Der Vorteil ist, dass Sie keinen benutzerdefinierten Decodierungs- / Codierungscode schreiben müssen.Beispielsweise:
quelle
Sie können implementieren.
quelle
Wenn Sie Ihre Codierungs- und Decodierungsmethoden nicht implementieren möchten, gibt es eine etwas schmutzige Lösung für Standardwerte.
Sie können Ihr neues Feld als implizit entpackt optional deklarieren und nach dem Dekodieren prüfen, ob es Null ist, und einen Standardwert festlegen.
Ich habe dies nur mit PropertyListEncoder getestet, aber ich denke, JSONDecoder funktioniert genauso.
quelle
Wenn Sie der Meinung sind, dass das Schreiben Ihrer eigenen Version von
init(from decoder: Decoder)
überwältigend ist, würde ich Ihnen raten, eine Methode zu implementieren, die die Eingabe überprüft, bevor Sie sie an den Decoder senden. Auf diese Weise haben Sie einen Ort, an dem Sie nach fehlenden Feldern suchen und Ihre eigenen Standardwerte festlegen können.Beispielsweise:
Und um ein Objekt von json aus zu initiieren, anstatt:
Init wird so aussehen:
In dieser speziellen Situation beschäftige ich mich lieber mit Optionen, aber wenn Sie eine andere Meinung haben, können Sie Ihre customDecode (:) -Methode werfen lassen
quelle
Ich bin auf diese Frage gestoßen und habe genau das Gleiche gesucht. Die Antworten, die ich fand, waren nicht sehr zufriedenstellend, obwohl ich befürchtete, dass die Lösungen hier die einzige Option sein würden.
In meinem Fall würde das Erstellen eines benutzerdefinierten Decoders eine Menge Boilerplate erfordern, die schwer zu warten wäre, sodass ich weiter nach anderen Antworten suchte.
Ich bin auf diesen Artikel gestoßen, der einen interessanten Weg zeigt, dies in einfachen Fällen mit a zu überwinden
@propertyWrapper
. Das Wichtigste für mich war, dass es wiederverwendbar war und nur minimales Refactoring des vorhandenen Codes erforderte.Der Artikel geht von einem Fall aus, in dem eine fehlende boolesche Eigenschaft standardmäßig auf false gesetzt werden soll, ohne dass dies fehlschlägt, zeigt aber auch andere unterschiedliche Varianten an. Sie können es genauer lesen, aber ich werde zeigen, was ich für meinen Anwendungsfall getan habe.
In meinem Fall hatte ich eine
array
, die ich als leer initialisieren wollte, wenn der Schlüssel fehlte.Also habe ich die folgenden
@propertyWrapper
und zusätzliche Erweiterungen deklariert :Der Vorteil dieser Methode besteht darin, dass Sie das Problem im vorhandenen Code leicht beheben können, indem Sie das einfach
@propertyWrapper
zur Eigenschaft hinzufügen . In meinem Fall:Ich hoffe, dies hilft jemandem, der sich mit dem gleichen Problem befasst.
AKTUALISIEREN:
Nachdem ich diese Antwort gepostet hatte, während ich mich weiter mit der Angelegenheit befasste, fand ich diesen anderen Artikel, aber vor allem die jeweilige Bibliothek, die einige gängige, einfach zu verwendende
@propertyWrapper
s für diese Art von Fällen enthält:https://github.com/marksands/BetterCodable
quelle