Czerpiąc inspirację z tej istoty , napisałem kilka rozszerzeń dla UnkeyedDecodingContainer
i KeyedDecodingContainer
. Link do mojej istoty można znaleźć tutaj . Używając tego kodu, możesz teraz zdekodować dowolne Array<Any>
lub Dictionary<String, Any>
za pomocą znanej składni:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
lub
let array: [Any] = try container.decode([Any].self, forKey: key)
Edycja: jest jedno zastrzeżenie, które znalazłem, które polega na dekodowaniu tablicy słowników [[String: Any]]
. Wymagana składnia jest następująca. Prawdopodobnie będziesz chciał zgłosić błąd zamiast wymusić rzucanie:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDYCJA 2: Jeśli chcesz po prostu przekonwertować cały plik do słownika, lepiej trzymaj się interfejsu API z JSONSerializacji, ponieważ nie znalazłem sposobu na rozszerzenie samego JSONDecodera w celu bezpośredniego dekodowania słownika.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Rozszerzenia
// 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>
jest sprawdzanie dla zagnieżdżonego tablicy. Więc jeśli masz strukturę danych, która wygląda następująco:[true, 452.0, ["a", "b", "c"] ]
ściągnęłaby zagnieżdżoną["a", "b", "c"]
tablicę.decode
Poprzez metodęUnkeyedDecodingContainer
„wyskakuje” poza elementem z pojemnika. Nie powinno powodować nieskończonej rekurencji.{"array": null}
. Więc twójguard contains(key)
test przejdzie, ale zawiesza się kilka wierszy później podczas próby zdekodowania wartości null dla klucza "tablica". Dlatego lepiej przed wywołaniem dodać jeszcze jeden warunek, aby sprawdzić, czy wartość faktycznie nie jest zerowadecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
próbować:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Ja też bawiłem się tym problemem i ostatecznie napisałem prostą bibliotekę do pracy z „ogólnymi typami JSON” . (Gdzie „ogólny” oznacza „bez struktury znanej z góry”). Głównym punktem jest reprezentacja ogólnego kodu JSON z konkretnym typem:
Ten typ może następnie zaimplementować
Codable
iEquatable
.źródło
Możesz stworzyć strukturę metadanych, która potwierdza
Decodable
protokół i użyćJSONDecoder
klasy do stworzenia obiektu z danych przy użyciu metody dekodowania, jak poniżejźródło
metadata
wartości. Może to być dowolny obiekt.metadata
może być dowolny obiekt JSON. Może to być pusty słownik lub dowolny słownik. "metadata": {} "metadata": {user_id: "id"} "metadata": {preference: {shows_value: true, language: "en"}} itd.Przyszedłem z nieco innym rozwiązaniem.
Załóżmy, że mamy coś więcej niż prostą
[String: Any]
do przeanalizowania, gdyby Any była tablicą, zagnieżdżonym słownikiem lub słownikiem tablic.Coś takiego:
Cóż, to jest moje rozwiązanie:
Wypróbuj za pomocą
źródło
Kiedy znalazłem starą odpowiedź, przetestowałem tylko prosty przypadek obiektu JSON, ale nie pusty, co spowoduje wyjątek środowiska uruchomieniowego, taki jak @slurmomatic i @zoul. Przepraszamy za ten problem.
Próbuję więc w inny sposób, mając prosty protokół JSONValue, zaimplementuj strukturę
AnyJSONValue
typu erasure i używam tego typu zamiastAny
. Oto implementacja.A oto jak go używać podczas dekodowania
Problem z tą sprawą polega na tym, że musimy zadzwonić
value.jsonValue as? Int
. Musimy poczekać, ażConditional Conformance
wylądujemy w Swift, co rozwiąże ten problem lub przynajmniej pomoże mu być lepszym.[Stara odpowiedź]
Publikuję to pytanie na forum Apple Developer i okazuje się, że jest to bardzo proste.
mogę zrobić
w inicjatorze.
Przede wszystkim źle było to przegapić.
źródło
Any
nie odpowiada,Decodable
więc nie jestem pewien, jak to jest poprawna odpowiedź.Jeśli używasz SwiftyJSON do analizowania JSON, możesz zaktualizować do wersji 4.1.0, która
Codable
obsługuje protokoły. Po prostu zadeklarujmetadata: JSON
i gotowe.źródło
Możesz rzucić okiem na BeyovaJSON
źródło
Najłatwiejszym i sugerowanym sposobem jest utworzenie oddzielnego modelu dla każdego słownika lub modelu w formacie JSON .
Oto co robię
Stosowanie:
** Użyłem opcji, aby być bezpiecznym podczas analizowania, można ją zmienić w razie potrzeby.
Przeczytaj więcej na ten temat
źródło
Zrobiłem kapsułę, aby ułatwić drogę kodujący dekodowanie +
[String: Any]
,[Any]
. A to zapewnia kodowanie lub dekodowanie opcjonalnych właściwości, tutaj https://github.com/levantAJ/AnyCodableJak tego użyć:
źródło
Tutaj jest bardziej ogólny (nie tylko
[String: Any]
, ale[Any]
można dekodować) i hermetyzowane podejście (do tego służy oddzielna jednostka) inspirowane odpowiedzią @loudmouth.Korzystanie z niego będzie wyglądać następująco:
JsonContainer
to jednostka pomocnicza, której używamy do zawijania dekodowania danych JSON do obiektu JSON (tablicy lub słownika) bez rozszerzania*DecodingContainer
(więc nie będzie kolidować z rzadkimi przypadkami, gdy obiekt JSON nie jest rozumiany[String: Any]
).Zwróć uwagę, że typy liczbowe i logiczne są
NSNumber
obsługiwane przez , w przeciwnym razie coś takiego nie zadziała:źródło
dekodować za pomocą dekodera i kluczy kodujących
źródło
źródło