Con algo de inspiración de esta esencia que encontré, escribí algunas extensiones para UnkeyedDecodingContainer
y KeyedDecodingContainer
. Puede encontrar un enlace a mi esencia aquí . Al usar este código, ahora puede decodificar cualquiera Array<Any>
o Dictionary<String, Any>
con la sintaxis familiar:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
o
let array: [Any] = try container.decode([Any].self, forKey: key)
Editar: hay una advertencia que he encontrado que es decodificar una matriz de diccionarios. [[String: Any]]
La sintaxis requerida es la siguiente. Es probable que desee lanzar un error en lugar de forzar el lanzamiento:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDITAR 2: Si simplemente desea convertir un archivo completo en un diccionario, es mejor que se quede con la API de JSONSerialization, ya que no he descubierto una manera de extender JSONDecoder para decodificar directamente un diccionario.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Las extensiones
// 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>
es la comprobación de un anidado matriz. Entonces, si tiene una estructura de datos que se parece a la siguiente:[true, 452.0, ["a", "b", "c"] ]
Extraería la["a", "b", "c"]
matriz anidada . Eldecode
método deUnkeyedDecodingContainer
"saca" el elemento del contenedor. No debería causar una recursividad infinita.{"array": null}
. Entoncesguard contains(key)
pasará, pero se bloqueará algunas líneas más tarde cuando intente decodificar el valor nulo para la clave "matriz". Por lo tanto, es mejor agregar una condición más para verificar si el valor en realidad no es nulo antes de llamardecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
intentarlo:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
También he jugado con este problema y finalmente escribí una biblioteca simple para trabajar con tipos "JSON genéricos" . (Donde "genérico" significa "sin estructura conocida de antemano".) El punto principal es representar el JSON genérico con un tipo concreto:
Este tipo puede implementar
Codable
yEquatable
.fuente
Puede crear una estructura de metadatos que confirme el
Decodable
protocolo y usar laJSONDecoder
clase para crear un objeto a partir de datos utilizando el método de decodificación como se muestra a continuaciónfuente
metadata
valor. Puede ser cualquier objeto arbitrario.metadata
puede ser cualquier objeto JSON. Entonces puede ser un diccionario vacío o cualquier diccionario. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferencia: {shows_value: true, language: "en"}} etc.Vine con una solución ligeramente diferente.
Supongamos que tenemos algo más que un simple
[String: Any]
análisis donde Any podría ser una matriz o un diccionario anidado o un diccionario de matrices.Algo como esto:
Bueno, esta es mi solución:
Pruébelo usando
fuente
Cuando encontré la respuesta anterior, solo probé un caso de objeto JSON simple pero no uno vacío, lo que causará una excepción de tiempo de ejecución como @slurmomatic y @zoul found. Perdón por este problema.
Así que trato de otra manera al tener un protocolo JSONValue simple, implementar la
AnyJSONValue
estructura de borrado de tipo y usar ese tipo en lugar deAny
. Aquí hay una implementación.Y así es como se usa al decodificar
El problema con este tema es que debemos llamar
value.jsonValue as? Int
. Tenemos que esperar hastaConditional Conformance
aterrizar en Swift, eso resolvería este problema o al menos ayudaría a mejorarlo.[Respuesta anterior]
Publico esta pregunta en el foro de desarrolladores de Apple y resulta que es muy fácil.
puedo hacer
en el inicializador.
Fue mi culpa perder eso en primer lugar.
fuente
Any
no se ajusta a,Decodable
así que no estoy seguro de cómo esta es la respuesta correcta.Si usa SwiftyJSON para analizar JSON, puede actualizar a 4.1.0 que tiene
Codable
soporte de protocolo. Simplemente declaremetadata: JSON
y estará listo.fuente
Podrías echar un vistazo a BeyovaJSON
fuente
La forma más fácil y sugerida es crear un modelo separado para cada diccionario o modelo que esté en JSON .
Esto es lo que hago
Uso:
** He utilizado opcional para estar en el lado seguro durante el análisis, se puede cambiar según sea necesario.
Leer más sobre este tema
fuente
He hecho una vaina para facilitar el camino de la decodificación + codifica
[String: Any]
,[Any]
. Y esto proporciona codificar o decodificar las propiedades opcionales, aquí https://github.com/levantAJ/AnyCodableCómo usarlo:
fuente
Aquí es más genérico (no solo
[String: Any]
, sino[Any]
enfoque puede decodificar) y encapsulado (para eso se usa una entidad separada) inspirado en la respuesta de @loudmouth.Usarlo se verá así:
JsonContainer
es una entidad auxiliar que usamos para encapsular la decodificación de datos JSON en un objeto JSON (ya sea una matriz o un diccionario) sin extender*DecodingContainer
(por lo que no interferirá con casos raros cuando un objeto JSON no se refiere a[String: Any]
).Tenga en cuenta que los tipos numéricos y booleanos están respaldados por
NSNumber
, de lo contrario, algo como esto no funcionará:fuente
decodificar usando decodificador y claves de codificación
fuente
fuente