2017-07-20 21 views

Odpowiedz

35

Jeśli nie przeszkadza trochę przesunięcia danych wokół można użyć coś takiego:

extension Encodable { 
    func asDictionary() throws -> [String: Any] { 
    let data = try JSONEncoder().encode(self) 
    guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { 
     throw NSError() 
    } 
    return dictionary 
    } 
} 

lub opcjonalnego varient

extension Encodable { 
    var dictionary: [String: Any]? { 
    guard let data = try? JSONEncoder().encode(self) else { return nil } 
    return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] } 
    } 
} 

Zakładając Foo zgodny Codable czy naprawdę Encodable możesz to zrobić.

let struct = Foo(a: 1, b: 2) 
let dict = try struct.asDictionary() 
let optionalDict = struct.dictionary 

Jeśli chcesz iść w drugą stronę (init(any)), przyjrzeć się tej Init an object conforming to Codable with a dictionary/array

3

Nie jestem pewien, czy to jest najlepszy sposób, ale na pewno można zrobić coś takiego:

struct Foo: Codable { 
    var a: Int 
    var b: Int 

    init(a: Int, b: Int) { 
     self.a = a 
     self.b = b 
    } 
} 

let foo = Foo(a: 1, b: 2) 
let dict = try JSONDecoder().decode([String: Int].self, from: JSONEncoder().encode(foo)) 
print(dict) 
+1

Działa to tylko w przypadku obiektów o wszystkich właściwościach tego samego rodzaju –

0

napisałem krótki gist obsłużyć to (nie przy użyciu protokołu do kodowania). Zachowaj ostrożność, nie sprawdzaj wartości typu i nie działa rekursywnie na wartości, które można kodować.

class DictionaryEncoder { 
    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 
    } 
} 

protocol DictionaryEncodable { 
    func encode(_ encoder: DictionaryEncoder) 
} 
-2

Przyjdź, aby myśleć o tym, pytanie nie ma odpowiedzi w ogólnym przypadku, ponieważ instancja Encodable może być coś nie serializable w słowniku, takich jak tablicy:

let payload = [1, 2, 3] 
let encoded = try JSONEncoder().encode(payload) // "[1,2,3]" 

Poza tym napisałem: something similar as a framework.

0

let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]

6

mam utworzyć bibliotekę o nazwie CodableFirebase i to Początkowym celem było go używać z Baza danych Firebase, ale faktycznie to, czego potrzebujesz: tworzy słownik lub dowolny inny typ, tak jak w JSONDecoder, ale nie musisz wykonywać podwójnej konwersji, tak jak robisz to w innych odpowiedziach. Tak to będzie wyglądać mniej więcej tak:

import CodableFirebase 

let model = Foo(a: 1, b: 2) 
let dict = try! FirebaseEncoder().encode(model) 
0

Zdecydowanie uważam, że jest jakaś wartość w po prostu będąc w stanie wykorzystać Codable zakodować do/ze słowników, bez zamiaru kiedykolwiek uderzających JSON/listy właściwości/cokolwiek. Istnieje wiele interfejsów API, które po prostu oddadzą ci słownik lub oczekują słownika, i miło jest móc je łatwo wymieniać z obiektami Swift lub obiektami, bez konieczności pisania nieskończonego kodu standardowego.

Gram w kółko z pewnym kodem opartym na źródle Foundation JSONEncoder.swift (które faktycznie implementuje kodowanie/dekodowanie słownika wewnętrznie, ale nie eksportuje go).

Kod można znaleźć tutaj: https://github.com/elegantchaos/DictionaryCoding

To wciąż dość szorstki, ale Rozszerzyliśmy to trochę tak, że na przykład, może wypełnić brakujące wartości z domyślnych przy dekodowaniu.