Wie kann ich Swift's Codable verwenden, um in ein Wörterbuch zu codieren?
109
Ich habe eine Struktur, die Swift 4 implementiert Codable. Gibt es eine einfache integrierte Möglichkeit, diese Struktur in ein Wörterbuch zu kodieren?
letstruct=Foo(a:1, b:2)let dict = something(struct)// now dict is["a":1,"b":2]
Hier sind einfache Implementierungen DictionaryEncoder/ DictionaryDecoderdass wickeln JSONEncoder, JSONDecoderund JSONSerialization, dass Griff auch Codierung / Decodierung Strategien ...
Vielen Dank! Die Alternative wäre die Verwendung der Vererbung, aber die aufrufende Site könnte den Typ nicht als Wörterbuch ableiten, da es zwei Funktionen mit unterschiedlichen Rückgabetypen geben würde.
user1046037
17
Ich habe eine Bibliothek namens CodableFirebase erstellt , deren ursprünglicher Zweck darin bestand, sie mit der Firebase-Datenbank zu verwenden, aber sie macht tatsächlich das, was Sie benötigen: Sie erstellt ein Wörterbuch oder einen anderen Typ wie in, JSONDecoderaber Sie müssen hier keine doppelte Konvertierung durchführen wie du es in anderen Antworten tust. Es würde also ungefähr so aussehen:
importCodableFirebaselet model =Foo(a:1, b:2)let dict =try!FirebaseEncoder().encode(model)
Dafür gibt es keinen eingebauten Weg. Wie oben beantwortet , können Sie die JSONEncoder+ JSONSerialization-Implementierung akzeptieren, wenn Sie keine Leistungsprobleme haben .
Aber ich würde lieber den Weg der Standardbibliothek gehen, um ein Encoder / Decoder-Objekt bereitzustellen.
classDictionaryEncoder{privatelet jsonEncoder =JSONEncoder()/// Encodes given Encodable value into an array or dictionary
func encode<T>(_ value: T)throws->Anywhere T:Encodable{let jsonData =try jsonEncoder.encode(value)returntryJSONSerialization.jsonObject(with: jsonData, options:.allowFragments)}}classDictionaryDecoder{privatelet jsonDecoder =JSONDecoder()/// Decodes given Decodable type from given array or dictionary
func decode<T>(_ type: T.Type, from json:Any)throws-> T where T:Decodable{let jsonData =tryJSONSerialization.data(withJSONObject: json, options:[])returntry jsonDecoder.decode(type, from: jsonData)}}
In einem Projekt habe ich die schnelle Reflexion verwendet. Aber seien Sie vorsichtig, verschachtelte codierbare Objekte werden auch dort nicht zugeordnet.
let dict =Dictionary(uniqueKeysWithValues:Mirror(reflecting: foo).children.map{($0.label!, $0.value)})
Ich denke definitiv, dass es einen gewissen Wert hat, nur in der Lage zu sein Codable, in / aus Wörterbüchern zu codieren, ohne die Absicht, jemals JSON / Plists / was auch immer zu treffen. Es gibt viele APIs, die Ihnen nur ein Wörterbuch zurückgeben oder ein Wörterbuch erwarten, und es ist schön, sie einfach mit Swift-Strukturen oder -Objekten austauschen zu können, ohne endlosen Boilerplate-Code schreiben zu müssen.
Ich habe mit Code herumgespielt, der auf der Foundation JSONEncoder.swift-Quelle basiert (die die Wörterbuchcodierung / -decodierung zwar intern implementiert, aber nicht exportiert).
Es ist immer noch ziemlich rau, aber ich habe es ein wenig erweitert, damit beispielsweise fehlende Werte beim Dekodieren mit Standardwerten gefüllt werden können.
Ich habe den PropertyListEncoder aus dem Swift-Projekt in einen DictionaryEncoder geändert , indem ich einfach die endgültige Serialisierung aus dem Wörterbuch in das Binärformat entfernt habe. Sie können das Gleiche selbst tun oder meinen Code von hier aus übernehmen
Ich habe einen kurzen Überblick darüber gegeben (ohne das Codable-Protokoll). Seien Sie vorsichtig, es überprüft keine Werte und funktioniert nicht rekursiv für codierbare Werte.
classDictionaryEncoder{var result:[String:Any]init(){
result =[:]}func encode(_ encodable:DictionaryEncodable)->[String:Any]{
encodable.encode(self)return result
}func encode<T, K>(_ value: T, key: K)where K:RawRepresentable, K.RawValue==String{
result[key.rawValue]= value
}}protocolDictionaryEncodable{func encode(_ encoder:DictionaryEncoder)}
In Codable gibt es keine direkte Möglichkeit, dies zu tun. Sie müssen das Encodable / Decodable-Protokoll für Ihre Struktur implementieren. Für Ihr Beispiel müssen Sie möglicherweise wie folgt schreiben
Wenn Sie sich das vorstellen, hat die Frage im allgemeinen Fall keine Antwort, da die EncodableInstanz möglicherweise nicht in ein Wörterbuch serialisierbar ist, z. B. ein Array:
let payload =[1,2,3]let encoded =tryJSONEncoder().encode(payload)//"[1,2,3]"
Antworten:
Wenn es Ihnen nichts ausmacht, Daten ein wenig zu verschieben, können Sie Folgendes verwenden:
Oder eine optionale Variante
Vorausgesetzt, es
Foo
entsprichtCodable
oder wirklich,Encodable
dann können Sie dies tun.Wenn Sie in die andere Richtung gehen möchten (
init(any)
), sehen Sie sich dieses Init an, ein Objekt, das Codable mit einem Wörterbuch / Array entsprichtquelle
Hier sind einfache Implementierungen
DictionaryEncoder
/DictionaryDecoder
dass wickelnJSONEncoder
,JSONDecoder
undJSONSerialization
, dass Griff auch Codierung / Decodierung Strategien ...Die Verwendung ist ähnlich wie
JSONEncoder
/JSONDecoder
…und
Der Einfachheit halber habe ich dies alles in ein Repo eingefügt ... https://github.com/ashleymills/SwiftDictionaryCoding
quelle
Ich habe eine Bibliothek namens CodableFirebase erstellt , deren ursprünglicher Zweck darin bestand, sie mit der Firebase-Datenbank zu verwenden, aber sie macht tatsächlich das, was Sie benötigen: Sie erstellt ein Wörterbuch oder einen anderen Typ wie in,
JSONDecoder
aber Sie müssen hier keine doppelte Konvertierung durchführen wie du es in anderen Antworten tust. Es würde also ungefähr so aussehen:quelle
Ich bin mir nicht sicher, ob es der beste Weg ist, aber Sie können definitiv etwas tun wie:
quelle
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
quelle
Dafür gibt es keinen eingebauten Weg. Wie oben beantwortet , können Sie die
JSONEncoder
+JSONSerialization
-Implementierung akzeptieren, wenn Sie keine Leistungsprobleme haben .Aber ich würde lieber den Weg der Standardbibliothek gehen, um ein Encoder / Decoder-Objekt bereitzustellen.
Sie können es mit folgendem Code versuchen:
Ich versuche hier, das Beispiel zu verkürzen. Im Produktionscode sollten Sie die Fehler angemessen behandeln.
quelle
In einem Projekt habe ich die schnelle Reflexion verwendet. Aber seien Sie vorsichtig, verschachtelte codierbare Objekte werden auch dort nicht zugeordnet.
quelle
Ich denke definitiv, dass es einen gewissen Wert hat, nur in der Lage zu sein
Codable
, in / aus Wörterbüchern zu codieren, ohne die Absicht, jemals JSON / Plists / was auch immer zu treffen. Es gibt viele APIs, die Ihnen nur ein Wörterbuch zurückgeben oder ein Wörterbuch erwarten, und es ist schön, sie einfach mit Swift-Strukturen oder -Objekten austauschen zu können, ohne endlosen Boilerplate-Code schreiben zu müssen.Ich habe mit Code herumgespielt, der auf der Foundation JSONEncoder.swift-Quelle basiert (die die Wörterbuchcodierung / -decodierung zwar intern implementiert, aber nicht exportiert).
Den Code finden Sie hier: https://github.com/elegantchaos/DictionaryCoding
Es ist immer noch ziemlich rau, aber ich habe es ein wenig erweitert, damit beispielsweise fehlende Werte beim Dekodieren mit Standardwerten gefüllt werden können.
quelle
Ich habe den PropertyListEncoder aus dem Swift-Projekt in einen DictionaryEncoder geändert , indem ich einfach die endgültige Serialisierung aus dem Wörterbuch in das Binärformat entfernt habe. Sie können das Gleiche selbst tun oder meinen Code von hier aus übernehmen
Es kann folgendermaßen verwendet werden:
quelle
Ich habe einen kurzen Überblick darüber gegeben (ohne das Codable-Protokoll). Seien Sie vorsichtig, es überprüft keine Werte und funktioniert nicht rekursiv für codierbare Werte.
quelle
In Codable gibt es keine direkte Möglichkeit, dies zu tun. Sie müssen das Encodable / Decodable-Protokoll für Ihre Struktur implementieren. Für Ihr Beispiel müssen Sie möglicherweise wie folgt schreiben
quelle
Ich habe hier einen Pod erstellt https://github.com/levantAJ/AnyCodable , um das Dekodieren und Kodieren zu erleichtern
[String: Any]
und[Any]
Und Sie können dekodieren und kodieren
[String: Any]
und[Any]
quelle
Wenn Sie SwiftyJSON verwenden , können Sie Folgendes tun:
JSON(data: JSONEncoder().encode(foo)).dictionaryObject
quelle
Hier ist eine protokollbasierte Lösung:
Und so geht's:
quelle
Hier ist Wörterbuch -> Objekt. Swift 5.
quelle
Wenn Sie sich das vorstellen, hat die Frage im allgemeinen Fall keine Antwort, da die
Encodable
Instanz möglicherweise nicht in ein Wörterbuch serialisierbar ist, z. B. ein Array:Davon abgesehen habe ich etwas Ähnliches als Framework geschrieben .
quelle