Mit etwas Inspiration von diesem Kern, den ich gefunden habe, habe ich einige Erweiterungen für UnkeyedDecodingContainer
und geschrieben KeyedDecodingContainer
. Einen Link zu meinem Kern finden Sie hier . Mit diesem Code können Sie jetzt eine beliebige Array<Any>
oder Dictionary<String, Any>
mit der bekannten Syntax dekodieren :
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
oder
let array: [Any] = try container.decode([Any].self, forKey: key)
Bearbeiten: Es gibt eine Einschränkung, die ich gefunden habe, nämlich das Dekodieren eines Arrays von Wörterbüchern. [[String: Any]]
Die erforderliche Syntax lautet wie folgt. Sie möchten wahrscheinlich einen Fehler auslösen, anstatt das Casting zu erzwingen:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDIT 2: Wenn Sie einfach eine ganze Datei in ein Wörterbuch konvertieren möchten, sollten Sie sich besser an die API von JSONSerialization halten, da ich keine Möglichkeit gefunden habe, JSONDecoder selbst zu erweitern, um ein Wörterbuch direkt zu dekodieren.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Die Erweiterungen
// Inspired by https://gist.github.com/mbuchetics/c9bc6c22033014aa0c550d3b4324411a
struct JSONCodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
self.init(stringValue: "\(intValue)")
self.intValue = intValue
}
}
extension KeyedDecodingContainer {
func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
var container = try self.nestedUnkeyedContainer(forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
var dictionary = Dictionary<String, Any>()
for key in allKeys {
if let boolValue = try? decode(Bool.self, forKey: key) {
dictionary[key.stringValue] = boolValue
} else if let stringValue = try? decode(String.self, forKey: key) {
dictionary[key.stringValue] = stringValue
} else if let intValue = try? decode(Int.self, forKey: key) {
dictionary[key.stringValue] = intValue
} else if let doubleValue = try? decode(Double.self, forKey: key) {
dictionary[key.stringValue] = doubleValue
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedDictionary
} else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedArray
}
}
return dictionary
}
}
extension UnkeyedDecodingContainer {
mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
var array: [Any] = []
while isAtEnd == false {
// See if the current value in the JSON array is `null` first and prevent infite recursion with nested arrays.
if try decodeNil() {
continue
} else if let value = try? decode(Bool.self) {
array.append(value)
} else if let value = try? decode(Double.self) {
array.append(value)
} else if let value = try? decode(String.self) {
array.append(value)
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
array.append(nestedDictionary)
} else if let nestedArray = try? decode(Array<Any>.self) {
array.append(nestedArray)
}
}
return array
}
mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
return try nestedContainer.decode(type)
}
}
UnkeyedDecodingContainer
‚sdecode(_ type: Array<Any>.Type) throws -> Array<Any>
für eine prüfen verschachtelte Array. Wenn Sie also eine Datenstruktur haben, die wie folgt aussieht:[true, 452.0, ["a", "b", "c"] ]
Sie würde das verschachtelte["a", "b", "c"]
Array ziehen. Diedecode
Methode einesUnkeyedDecodingContainer
"Pops" entfernt das Element aus dem Container. Es sollte keine unendliche Rekursion verursachen.{"array": null}
. Sieguard contains(key)
werden also bestehen, aber einige Zeilen später abstürzen, wenn Sie versuchen, den Nullwert für den Schlüssel "array" zu dekodieren. Es ist daher besser, vor dem Aufruf eine weitere Bedingung hinzuzufügen, um zu überprüfen, ob der Wert tatsächlich nicht null istdecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
versuchen:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Ich habe auch mit diesem Problem gespielt und schließlich eine einfache Bibliothek für die Arbeit mit „generischen JSON“ -Typen geschrieben . (Wobei "generisch" "ohne vorher bekannte Struktur" bedeutet.) Hauptpunkt ist die Darstellung des generischen JSON mit einem konkreten Typ:
Dieser Typ kann dann
Codable
und implementierenEquatable
.quelle
Sie können eine Metadatenstruktur erstellen, die das
Decodable
Protokoll bestätigt, und mithilfe derJSONDecoder
Klasse Objekte aus Daten erstellen, indem Sie die unten beschriebene Dekodierungsmethode verwendenquelle
metadata
Wertes nicht kenne . Es kann ein beliebiges Objekt sein.metadata
kann ein beliebiges JSON-Objekt sein. Es kann also ein leeres Wörterbuch oder ein beliebiges Wörterbuch sein. "Metadaten": {} "Metadaten": {Benutzer_ID: "ID"} "Metadaten": {Präferenz: {Shows_Wert: Wahr, Sprache: "En"}} usw.Ich kam mit einer etwas anderen Lösung.
Nehmen wir an, wir haben mehr als nur eine einfache
[String: Any]
Analyse, bei der Any ein Array oder ein verschachteltes Wörterbuch oder ein Wörterbuch von Arrays sein könnte.Etwas wie das:
Nun, das ist meine Lösung:
Probieren Sie es mit
quelle
Als ich die alte Antwort gefunden habe, habe ich nur einen einfachen JSON-Objektfall getestet, aber keinen leeren, der eine Laufzeitausnahme wie @slurmomatic und @zoul gefunden hat. Entschuldigung für dieses Problem.
Also versuche ich es auf andere Weise, indem ich ein einfaches JSONValue-Protokoll habe, die
AnyJSONValue
Struktur zum Löschen des Typs implementiere und stattdessen diesen Typ verwendeAny
. Hier ist eine Implementierung.Und hier erfahren Sie, wie Sie es beim Dekodieren verwenden
Das Problem mit diesem Problem ist, dass wir anrufen müssen
value.jsonValue as? Int
. Wir müssen warten, bis wirConditional Conformance
in Swift landen, um dieses Problem zu lösen oder zumindest zu helfen, besser zu werden.[Alte Antwort]
Ich poste diese Frage im Apple Developer Forum und es stellt sich heraus, dass es sehr einfach ist.
ich kann
im Initialisierer.
Es war mein schlechtes, das überhaupt zu verpassen.
quelle
Any
entspricht nicht,Decodable
daher bin ich mir nicht sicher, wie dies die richtige Antwort ist.Wenn Sie SwiftyJSON zum Parsen von JSON verwenden, können Sie auf 4.1.0 aktualisieren, das
Codable
Protokollunterstützung bietet . Einfach deklarierenmetadata: JSON
und fertig.quelle
Vielleicht sehen Sie sich BeyovaJSON an
quelle
Der einfachste und empfohlene Weg besteht darin , für jedes Wörterbuch oder Modell in JSON ein separates Modell zu erstellen .
Hier ist was ich tue
Verwendung:
** Ich habe optional verwendet, um beim Parsen auf der sicheren Seite zu sein. Kann bei Bedarf geändert werden.
Lesen Sie mehr zu diesem Thema
quelle
Ich habe eine pod gemacht, wie die Decodierung + codiert , zu erleichtern
[String: Any]
,[Any]
. Und dies ermöglicht das Codieren oder Decodieren der optionalen Eigenschaften, hier https://github.com/levantAJ/AnyCodableWie man es benutzt:
quelle
Hier ist ein allgemeinerer (nicht nur
[String: Any]
, aber[Any]
auch entschlüsselbarer) und gekapselter Ansatz (dafür wird eine separate Entität verwendet), der von der Antwort von @loudmouth inspiriert ist.Die Verwendung sieht folgendermaßen aus:
JsonContainer
ist eine Hilfsentität, die wir verwenden, um die Dekodierung von JSON-Daten in ein JSON-Objekt (entweder Array oder Wörterbuch) zu verpacken, ohne sie zu erweitern*DecodingContainer
(damit seltene Fälle, in denen ein JSON-Objekt nicht gemeint ist, nicht beeinträchtigt werden[String: Any]
).Beachten Sie, dass numerische und boolesche Typen von unterstützt werden
NSNumber
, sonst funktioniert so etwas nicht:quelle
Decodieren mit Decoder und Codierschlüsseln
quelle
quelle