Wielkie dzięki !, alternatywą byłoby użycie dziedziczenia, ale strona wywołująca nie byłaby w stanie wywnioskować typu jako słownika, ponieważ byłyby 2 funkcje o różnych typach zwracanych.
user1046037
17
Utworzyłem bibliotekę o nazwie CodableFirebase i początkowym celem było użycie jej z bazą danych Firebase, ale faktycznie robi to, czego potrzebujesz: tworzy słownik lub dowolny inny typ, tak jak w, JSONDecoderale nie musisz tutaj wykonywać podwójnej konwersji tak jak w innych odpowiedziach. Więc wyglądałoby to mniej więcej tak:
importCodableFirebaselet model =Foo(a:1, b:2)let dict =try!FirebaseEncoder().encode(model)
Nie ma na to sposobu. Zgodnie z powyższą odpowiedzią, jeśli nie masz problemów z wydajnością, możesz zaakceptować plikJSONEncoder + JSONSerializationwdrożenie.
Ale wolałbym raczej użyć standardowej biblioteki, aby zapewnić obiekt kodera / dekodera.
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)}}
Zdecydowanie uważam, że po prostu możliwość używania ma jakąś wartość Codable kodowania do / ze słowników, bez zamiaru kiedykolwiek uderzania w JSON / Plists / cokolwiek. Istnieje wiele interfejsów API, które po prostu zwracają słownik lub oczekują słownika, i miło jest móc łatwo wymieniać je ze strukturami lub obiektami Swift, bez konieczności pisania niekończącego się standardowego kodu.
Bawiłem się kodem opartym na źródle Foundation JSONEncoder.swift (które faktycznie implementuje wewnętrzne kodowanie / dekodowanie słownika, ale go nie eksportuje).
Zmodyfikowałem PropertyListEncoder z projektu Swift na DictionaryEncoder, po prostu usuwając końcową serializację ze słownika do formatu binarnego. Możesz zrobić to samo samodzielnie lub pobrać mój kod stąd
Napisałem krótkie streszczenie, jak sobie z tym poradzić (nie używając protokołu Codable). Uważaj, nie sprawdza typu żadnych wartości i nie działa rekurencyjnie na wartościach, które można zakodować.
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)}
W Codable nie ma na to prostego sposobu. Musisz zaimplementować protokół Encodable / Decodable dla swojej struktury. Na przykład możesz potrzebować napisać jak poniżej
Jeśli się nad tym zastanowić, to pytanie nie ma odpowiedzi w ogólnym przypadku, ponieważ Encodableinstancja może być czymś, czego nie można serializować w słowniku, na przykład tablicą:
let payload =[1,2,3]let encoded =tryJSONEncoder().encode(payload)//"[1,2,3]"
Odpowiedzi:
Jeśli nie przeszkadza ci trochę przesunięcie danych, możesz użyć czegoś takiego:
Lub opcjonalny wariant
Zakładając, że się
Foo
zgadzaCodable
lub naprawdę,Encodable
możesz to zrobić.Jeśli chcesz iść w drugą stronę (
init(any)
), spójrz na to Inituj obiekt zgodny z Codable ze słownikiem / tablicąźródło
Oto proste implementacje
DictionaryEncoder
/DictionaryDecoder
które owijająJSONEncoder
,JSONDecoder
orazJSONSerialization
, że również obsługiwać kodowanie / dekodowanie strategie ...Użycie jest podobne do
JSONEncoder
/JSONDecoder
…i
Dla wygody umieściłem to wszystko w repozytorium… https://github.com/ashleymills/SwiftDictionaryCoding
źródło
Utworzyłem bibliotekę o nazwie CodableFirebase i początkowym celem było użycie jej z bazą danych Firebase, ale faktycznie robi to, czego potrzebujesz: tworzy słownik lub dowolny inny typ, tak jak w,
JSONDecoder
ale nie musisz tutaj wykonywać podwójnej konwersji tak jak w innych odpowiedziach. Więc wyglądałoby to mniej więcej tak:źródło
Nie jestem pewien, czy to najlepszy sposób, ale na pewno możesz zrobić coś takiego:
źródło
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
źródło
Nie ma na to sposobu. Zgodnie z powyższą odpowiedzią, jeśli nie masz problemów z wydajnością, możesz zaakceptować plik
JSONEncoder
+JSONSerialization
wdrożenie.Ale wolałbym raczej użyć standardowej biblioteki, aby zapewnić obiekt kodera / dekodera.
Możesz spróbować z następującym kodem:
Staram się tutaj na siłę skrócić przykład. W kodzie produkcyjnym należy odpowiednio obsługiwać błędy.
źródło
W niektórych projektach korzystam z szybkiej refleksji. Ale uważaj, zagnieżdżone obiekty kodowalne również nie są tam mapowane.
źródło
Zdecydowanie uważam, że po prostu możliwość używania ma jakąś wartość
Codable
kodowania do / ze słowników, bez zamiaru kiedykolwiek uderzania w JSON / Plists / cokolwiek. Istnieje wiele interfejsów API, które po prostu zwracają słownik lub oczekują słownika, i miło jest móc łatwo wymieniać je ze strukturami lub obiektami Swift, bez konieczności pisania niekończącego się standardowego kodu.Bawiłem się kodem opartym na źródle Foundation JSONEncoder.swift (które faktycznie implementuje wewnętrzne kodowanie / dekodowanie słownika, ale go nie eksportuje).
Kod można znaleźć tutaj: https://github.com/elegantchaos/DictionaryCoding
Nadal jest dość szorstki, ale nieco go rozszerzyłem, aby na przykład mógł uzupełniać brakujące wartości domyślnymi wartościami podczas dekodowania.
źródło
Zmodyfikowałem PropertyListEncoder z projektu Swift na DictionaryEncoder, po prostu usuwając końcową serializację ze słownika do formatu binarnego. Możesz zrobić to samo samodzielnie lub pobrać mój kod stąd
Można go używać w następujący sposób:
źródło
Napisałem krótkie streszczenie, jak sobie z tym poradzić (nie używając protokołu Codable). Uważaj, nie sprawdza typu żadnych wartości i nie działa rekurencyjnie na wartościach, które można zakodować.
źródło
W Codable nie ma na to prostego sposobu. Musisz zaimplementować protokół Encodable / Decodable dla swojej struktury. Na przykład możesz potrzebować napisać jak poniżej
źródło
Zrobiłem tutaj pod https://github.com/levantAJ/AnyCodable, aby ułatwić dekodowanie i kodowanie
[String: Any]
oraz[Any]
Jesteś w stanie dekodować i kodować
[String: Any]
oraz[Any]
źródło
Jeśli używasz SwiftyJSON , możesz zrobić coś takiego:
JSON(data: JSONEncoder().encode(foo)).dictionaryObject
źródło
Oto rozwiązanie oparte na protokole:
A oto jak z niego korzystać:
źródło
Oto słownik -> obiekt. Szybki 5.
źródło
Jeśli się nad tym zastanowić, to pytanie nie ma odpowiedzi w ogólnym przypadku, ponieważ
Encodable
instancja może być czymś, czego nie można serializować w słowniku, na przykład tablicą:Poza tym napisałem coś podobnego jako framework .
źródło