En me inspirant de cet essentiel que j'ai trouvé, j'ai écrit des extensions pour UnkeyedDecodingContainer
et KeyedDecodingContainer
. Vous pouvez trouver un lien vers mon essence ici . En utilisant ce code, vous pouvez maintenant décoder tout Array<Any>
ou Dictionary<String, Any>
avec la syntaxe familière:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
ou
let array: [Any] = try container.decode([Any].self, forKey: key)
Edit: il y a une mise en garde que j'ai trouvée qui décode un tableau de dictionnaires [[String: Any]]
La syntaxe requise est la suivante. Vous voudrez probablement lancer une erreur au lieu de lancer de force:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDIT 2: Si vous voulez simplement convertir un fichier entier en dictionnaire, vous feriez mieux de vous en tenir à l'api de JSONSerialization car je n'ai pas trouvé de moyen d'étendre JSONDecoder lui-même pour décoder directement un dictionnaire.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Les extensions
// 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>
est la vérification d'un imbriqué tableau. Donc, si vous avez une structure de données qui ressemble à ce qui suit:[true, 452.0, ["a", "b", "c"] ]
Cela extrairait le["a", "b", "c"]
tableau imbriqué . Ladecode
méthode d'unUnkeyedDecodingContainer
"saute" de l'élément du conteneur. Cela ne devrait pas provoquer de récursivité infinie.{"array": null}
. Donc, vousguard contains(key)
passerez mais il plantera quelques lignes plus tard en essayant de décoder la valeur nulle pour la clé "array". Il est donc préférable d'ajouter une condition supplémentaire pour vérifier si la valeur n'est réellement pas nulle avant d'appelerdecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
essayer:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
J'ai également joué avec ce problème et j'ai finalement écrit une bibliothèque simple pour travailler avec des types «JSON génériques» . (Où «générique» signifie «sans structure connue à l'avance».) Le point principal est de représenter le JSON générique avec un type concret:
Ce type peut alors implémenter
Codable
etEquatable
.la source
Vous pouvez créer une structure de métadonnées qui confirme le
Decodable
protocole et utiliser laJSONDecoder
classe pour créer un objet à partir de données en utilisant la méthode de décodage comme ci-dessousla source
metadata
valeur. Cela peut être n'importe quel objet arbitraire.metadata
peut être n'importe quel objet JSON. Cela peut donc être un dictionnaire vide ou n'importe quel dictionnaire. "metadata": {} "metadata": {user_id: "id"} "metadata": {préférence: {shows_value: true, language: "en"}} etc.Je suis venu avec une solution légèrement différente.
Supposons que nous ayons quelque chose de plus qu'un simple
[String: Any]
à analyser où Any pourrait être un tableau ou un dictionnaire imbriqué ou un dictionnaire de tableaux.Quelque chose comme ça:
Eh bien, voici ma solution:
Essayez-le en utilisant
la source
Lorsque j'ai trouvé l'ancienne réponse, je n'ai testé qu'un cas d'objet JSON simple mais pas un cas vide, ce qui provoquera une exception d'exécution comme @slurmomatic et @zoul found. Désolé pour ce problème.
J'essaye donc une autre manière en ayant un protocole JSONValue simple, implémente la
AnyJSONValue
structure d'effacement de type et utilise ce type à la place deAny
. Voici une implémentation.Et voici comment l'utiliser lors du décodage
Le problème avec cette question est que nous devons appeler
value.jsonValue as? Int
. Nous devons attendre laConditional Conformance
terre à Swift, cela résoudrait ce problème ou au moins l'aiderait à s'améliorer.[Ancienne réponse]
Je poste cette question sur le forum Apple Developer et il s'avère que c'est très simple.
Je peux faire
dans l'initialiseur.
C'était mon mal de rater ça en premier lieu.
la source
Any
n'est pas conforme à,Decodable
donc je ne sais pas comment c'est la bonne réponse.Si vous utilisez SwiftyJSON pour analyser JSON, vous pouvez mettre à jour vers la version 4.1.0 qui prend en
Codable
charge le protocole. Déclarez simplementmetadata: JSON
et vous êtes prêt.la source
Vous pourriez jeter un œil à BeyovaJSON
la source
La méthode la plus simple et suggérée consiste à créer un modèle distinct pour chaque dictionnaire ou modèle en JSON .
Voici ce que je fais
Usage:
** J'ai utilisé optionnel pour être en sécurité lors de l'analyse, peut être modifié au besoin.
En savoir plus sur ce sujet
la source
Je l' ai fait une nacelle pour faciliter la façon dont le décodage + encodage
[String: Any]
,[Any]
. Et cela permet d'encoder ou de décoder les propriétés optionnelles, ici https://github.com/levantAJ/AnyCodableComment l'utiliser:
la source
Voici plus générique (non seulement
[String: Any]
, mais[Any]
approche décodée) et encapsulée (une entité distincte est utilisée pour cela) inspirée de @loudmouth answer.Son utilisation ressemblera à:
JsonContainer
est une entité d'assistance que nous utilisons pour encapsuler le décodage des données JSON en objet JSON (tableau ou dictionnaire) sans étendre*DecodingContainer
(afin de ne pas interférer avec de rares cas où un objet JSON n'est pas désigné par[String: Any]
).Notez que les types numériques et booléens sont soutenus par
NSNumber
, sinon quelque chose comme ça ne fonctionnera pas:la source
décoder à l'aide du décodeur et des clés de codage
la source
la source