Ich verwende die Codable
Protokolle, um JSON von einer Web-API zu dekodieren. Mein Swift
Datenmodell für diese API enthält Klassenvererbung (Unterklassen) und Komposition (Objekte als Eigenschaften anderer Objekte). In JSON kann derselbe Eigenschaftsname ein vollständiges Objekt oder eine einzelne Zeichenfolge darstellen, die die ID dieses Objekts in einer Datenbank angibt.decoder.container (keyedBy :) löst DecodingError.typeMismatch-Fehler aus. Codierbare Wanze?
meines Wissens das einzige Muster, diese Art von JSON für den Umgang mit Codable
verwendet, ist die Decodierung zu tun „von Hand“ innerhalb des initializer des Objekts init(from decoder: Decoder)
und zunächst versuchen, das gesamte Objekt zu entschlüsseln. Sollte dies fehlschlagen (indem ein Fehler geworfen wird, der abgefangen werden muss), dann versuchen Sie erneut, die gleiche Eigenschaft wie eine String
zu dekodieren.
Dies funktioniert gut, solange das Objekt, das die Eigenschaft varient enthält, keine Unterklasse einer anderen Klasse Decodable
ist. Wenn dies der Fall ist, löst das Dekodieren der Eigenschaften der Basisklasse den Fehler DecodingError.typeMismatch
beim Aufruf der Funktion decoder.container(keyedBy:)
aus.
Siehe meinen Beispielcode unten.
Ist das ein bekannter Fehler? Und/oder fehlt mir in dieser Situation eine alternative Methode der Entschlüsselung?
Im Übrigen wird derselbe Fehler innerhalb einer einzigen Funktion ausgelöst, wenn decoder.container(keyedBy:)
aufgerufen wird, nachdem ein DecodingError.typeMismatch
Fehler ausgelöst wurde, auch wenn dieser Fehler abgefangen wurde.
import Foundation
// A `Base` class
class Base: Codable {
var baseProperty: String? = "baseProperty"
init() {}
private enum CodingKeys: String, CodingKey { case baseProperty }
required init(from decoder: Decoder) throws {
//===>> The next line will throw DecodingError.typeMismatch
let container = try decoder.container(keyedBy: CodingKeys.self)
baseProperty = try container.decode(String.self, forKey: .baseProperty)
}
}
// A Subclass of `Base`
class Sub: Base {
// An `Other` class which is a property of the `Sub` class
class Other: Codable { var id: String? = "otherID" }
var subProperty: Other? = nil
override init() { super.init() }
private enum CodingKeys: String, CodingKey { case subProperty }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do { subProperty = try container.decode(Other.self,
forKey: .subProperty)
}
catch { // We didn't find a whole `Other` object in the JSON; look for a `String` instead
let s = try container.decode(String.self, forKey: .subProperty)
subProperty = Other()
subProperty?.id = s
}
try super.init(from: decoder)
}
}
// Some sample JSON data:
let json = """
{"baseProperty" : "baseProperty",
"subProperty" : "someIDString"}
""".data(using: .utf8)!
// MAIN program -----------------------------------------------------
// Decode the JSON to produce a new Sub class instance
do {
_ = try JSONDecoder().decode(Sub.self, from: json)
}
catch DecodingError.typeMismatch(_, let context) {
print("DecodingError.typeMismatch: \(context.debugDescription)")
print("DecodingError.Context: codingPath:")
for i in 0..<context.codingPath.count { print(" [\(i)] = \(context.codingPath[i])") }
}
OMG Ich schulde dir ein Bier. ;-) –