Cómo decodificar una propiedad con el tipo de diccionario JSON en el protocolo decodificable Swift 4

103

Digamos que tengo Customerun tipo de datos que contiene una metadatapropiedad que puede contener cualquier diccionario JSON en el objeto del cliente

struct Customer {
  let id: String
  let email: String
  let metadata: [String: Any]
}

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "[email protected]",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

La metadatapropiedad puede ser cualquier objeto de mapa JSON arbitrario.

Antes de que pueda convertir la propiedad desde un JSON deserializado, NSJSONDeserializationpero con el nuevo Decodableprotocolo Swift 4 , todavía no puedo pensar en una forma de hacerlo.

¿Alguien sabe cómo lograr esto en Swift 4 con el protocolo Decodable?

Pitiphong Phongpattranont
fuente

Respuestas:

89

Con algo de inspiración de esta esencia que encontré, escribí algunas extensiones para UnkeyedDecodingContainery 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)
    }
}
bocaza
fuente
Interesante, probaré esta esencia y actualizaré el resultado para ti @loudmouth
Pitiphong Phongpattranont
@PitiphongPhongpattranont, ¿este código te funcionó?
bocazas
1
@JonBrooks la última condición en el en UnkeyedDecodingContainer'S decode(_ 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 . El decodemétodo de UnkeyedDecodingContainer"saca" el elemento del contenedor. No debería causar una recursividad infinita.
bocazas
1
@loudmouth que es posible tener valores nulos para las llaves en JSON: {"array": null}. Entonces guard 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 llamar decode.
chebur
2
Encontré una solución: en lugar de } 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) {
Jon Brooks
23

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:

public enum JSON {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
    case null
}

Este tipo puede implementar Codabley Equatable.

zoul
fuente
13

Puede crear una estructura de metadatos que confirme el Decodableprotocolo y usar la JSONDecoderclase para crear un objeto a partir de datos utilizando el método de decodificación como se muestra a continuación

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "[email protected]",
    "metadata": [
        "link_id": "linked-id",
        "buy_count": 4
    ]
]

struct Customer: Decodable {
    let object: String
    let id: String
    let email: String
    let metadata: Metadata
}

struct Metadata: Decodable {
    let link_id: String
    let buy_count: Int
}

let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

let decoder = JSONDecoder()
do {
    let customer = try decoder.decode(Customer.self, from: data)
    print(customer)
} catch {
    print(error.localizedDescription)
}
Suhit Patil
fuente
10
No, no puedo, ya que no conozco la estructura del metadatavalor. Puede ser cualquier objeto arbitrario.
Pitiphong Phongpattranont
¿Quiere decir que puede ser del tipo Array o Dictionary?
Suhit Patil
¿Puede dar un ejemplo o agregar más explicaciones sobre la estructura de los metadatos?
Suhit Patil
2
El valor de metadatapuede 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.
Pitiphong Phongpattranont
una opción posible sería usar todos los parámetros en la estructura de metadatos como opcionales y enumerar todos los valores posibles en la estructura de metadatos como struct metadata {var user_id: String? preferencia var: String? }
Suhit Patil
8

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:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

Bueno, esta es mi solución:

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

Pruébelo usando

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)
Giuseppe Lanza
fuente
6

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 AnyJSONValueestructura de borrado de tipo y usar ese tipo en lugar de Any. Aquí hay una implementación.

public protocol JSONType: Decodable {
    var jsonValue: Any { get }
}

extension Int: JSONType {
    public var jsonValue: Any { return self }
}
extension String: JSONType {
    public var jsonValue: Any { return self }
}
extension Double: JSONType {
    public var jsonValue: Any { return self }
}
extension Bool: JSONType {
    public var jsonValue: Any { return self }
}

public struct AnyJSONType: JSONType {
    public let jsonValue: Any

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let intValue = try? container.decode(Int.self) {
            jsonValue = intValue
        } else if let stringValue = try? container.decode(String.self) {
            jsonValue = stringValue
        } else if let boolValue = try? container.decode(Bool.self) {
            jsonValue = boolValue
        } else if let doubleValue = try? container.decode(Double.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Array<AnyJSONType>.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Dictionary<String, AnyJSONType>.self) {
            jsonValue = doubleValue
        } else {
            throw DecodingError.typeMismatch(JSONType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON tyep"))
        }
    }
}

Y así es como se usa al decodificar

metadata = try container.decode ([String: AnyJSONValue].self, forKey: .metadata)

El problema con este tema es que debemos llamar value.jsonValue as? Int. Tenemos que esperar hasta Conditional Conformanceaterrizar 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

metadata = try container.decode ([String: Any].self, forKey: .metadata)

en el inicializador.

Fue mi culpa perder eso en primer lugar.

Pitiphong Phongpattranont
fuente
4
Podría publicar el enlace a la pregunta en Apple Developer. Anyno se ajusta a, Decodableasí que no estoy seguro de cómo esta es la respuesta correcta.
Reza Shirazian
@RezaShirazian Eso es lo que pensé en primer lugar. Pero resulta que Dictionary se ajusta a Encodable cuando sus claves se ajustan a Hashable y no dependen de sus valores. Puede abrir el encabezado Diccionario y verlo usted mismo. Diccionario de extensión: Codificable donde Clave: Diccionario de extensión Hashable: Decodificable donde Clave: Foros hashable.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont
6
actualmente esto no funciona. "Dictionary <String, Any> no se ajusta a Decodable porque Any no cumple con Decodable"
mbuchetics
Resulta que funciona. Lo estoy usando en mi código. Debe comprender que no hay forma de expresar el requisito de que "el valor del diccionario debe cumplir con el protocolo decodificable para que el diccionario se ajuste al protocolo decodificable" ahora. Esa es la "Conformidad condicional" que aún no está implementada en Swift 4. Creo que está bien por ahora, ya que hay muchas limitaciones en el Swift Type System (y en los genéricos). Así que esto funciona por ahora, pero cuando el Swift Type System mejore en el futuro (especialmente cuando se implemente la conformidad condicional), esto no debería funcionar.
Pitiphong Phongpattranont
3
No me funciona a partir de Xcode 9 beta 5. Se compila, pero explota en tiempo de ejecución: Dictionary <String, Any> no se ajusta a Decodable porque Any no se ajusta a Decodable.
zoul
6

Si usa SwiftyJSON para analizar JSON, puede actualizar a 4.1.0 que tiene Codablesoporte de protocolo. Simplemente declare metadata: JSONy estará listo.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}
Allen Huang
fuente
No sé por qué se rechazó esta respuesta. Es totalmente válido y resuelve el problema.
Leonid Usov
Parece ser bueno para la migración de SwiftyJSON a Decodable
Michał Ziobro
Esto no resuelve cómo luego analizar el json de metadatos, que era el problema original.
llamacorn
1

Podrías echar un vistazo a BeyovaJSON

import BeyovaJSON

struct Customer: Codable {
  let id: String
  let email: String
  let metadata: JToken
}

//create a customer instance

customer.metadata = ["link_id": "linked-id","buy_count": 4]

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted 
print(String(bytes: try! encoder.encode(customer), encoding: .utf8)!)
canius
fuente
Oh, muy bien. Usándolo para recibir un JSON genérico como JToken, agregando algunos valores y volviendo al servidor. Muy bueno de verdad. Ese es un trabajo increíble que has hecho :)
Vitor Hugo Schwaab
1

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

//Model for dictionary **Metadata**

struct Metadata: Codable {
    var link_id: String?
    var buy_count: Int?
}  

//Model for dictionary **Customer**

struct Customer: Codable {
   var object: String?
   var id: String?
   var email: String?
   var metadata: Metadata?
}

//Here is our decodable parser that decodes JSON into expected model

struct CustomerParser {
    var customer: Customer?
}

extension CustomerParser: Decodable {

//keys that matches exactly with JSON
enum CustomerKeys: String, CodingKey {
    case object = "object"
    case id = "id"
    case email = "email"
    case metadata = "metadata"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CustomerKeys.self) // defining our (keyed) container

    let object: String = try container.decode(String.self, forKey: .object) // extracting the data
    let id: String = try container.decode(String.self, forKey: .id) // extracting the data
    let email: String = try container.decode(String.self, forKey: .email) // extracting the data

   //Here I have used metadata model instead of dictionary [String: Any]
    let metadata: Metadata = try container.decode(Metadata.self, forKey: .metadata) // extracting the data

    self.init(customer: Customer(object: object, id: id, email: email, metadata: metadata))

    }
}

Uso:

  if let url = Bundle.main.url(forResource: "customer-json-file", withExtension: "json") {
        do {
            let jsonData: Data =  try Data(contentsOf: url)
            let parser: CustomerParser = try JSONDecoder().decode(CustomerParser.self, from: jsonData)
            print(parser.customer ?? "null")

        } catch {

        }
    }

** 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

minhazur
fuente
1
¡Su respuesta es la apropiada para Swift 4.1 con seguridad y la primera línea de su publicación está muerta! Suponiendo que los datos provienen de un servicio web. puede modelar objetos anidados simples y luego usar la sintaxis de puntos para tomar cada uno. Vea la respuesta de suhit a continuación.
David H
1

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/AnyCodable

pod 'DynamicCodable', '1.0'

Cómo usarlo:

import DynamicCodable

struct YourObject: Codable {
    var dict: [String: Any]
    var array: [Any]
    var optionalDict: [String: Any]?
    var optionalArray: [Any]?

    enum CodingKeys: String, CodingKey {
        case dict
        case array
        case optionalDict
        case optionalArray
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dict = try values.decode([String: Any].self, forKey: .dict)
        array = try values.decode([Any].self, forKey: .array)
        optionalDict = try values.decodeIfPresent([String: Any].self, forKey: .optionalDict)
        optionalArray = try values.decodeIfPresent([Any].self, forKey: .optionalArray)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(dict, forKey: .dict)
        try container.encode(array, forKey: .array)
        try container.encodeIfPresent(optionalDict, forKey: .optionalDict)
        try container.encodeIfPresent(optionalArray, forKey: .optionalArray)
    }
}
Tai Le
fuente
0

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í:

extension Customer: Decodable {
  public init(from decoder: Decoder) throws {
    let selfContainer = try decoder.container(keyedBy: CodingKeys.self)
    id = try selfContainer.decode(.id)
    email = try selfContainer.decode(.email)
    let metadataContainer: JsonContainer = try selfContainer.decode(.metadata)
    guard let metadata = metadataContainer.value as? [String: Any] else {
      let context = DecodingError.Context(codingPath: [CodingKeys.metadata], debugDescription: "Expected '[String: Any]' for 'metadata' key")
      throw DecodingError.typeMismatch([String: Any].self, context)
    }
    self.metadata = metadata
  }

  private enum CodingKeys: String, CodingKey {
    case id, email, metadata
  }
}

JsonContaineres 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]).

struct JsonContainer {

  let value: Any
}

extension JsonContainer: Decodable {

  public init(from decoder: Decoder) throws {
    if let keyedContainer = try? decoder.container(keyedBy: Key.self) {
      var dictionary = [String: Any]()
      for key in keyedContainer.allKeys {
        if let value = try? keyedContainer.decode(Bool.self, forKey: key) {
          // Wrapping numeric and boolean types in `NSNumber` is important, so `as? Int64` or `as? Float` casts will work
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Int64.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Double.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(String.self, forKey: key) {
          dictionary[key.stringValue] = value
        } else if (try? keyedContainer.decodeNil(forKey: key)) ?? false {
          // NOP
        } else if let value = try? keyedContainer.decode(JsonContainer.self, forKey: key) {
          dictionary[key.stringValue] = value.value
        } else {
          throw DecodingError.dataCorruptedError(forKey: key, in: keyedContainer, debugDescription: "Unexpected value for \(key.stringValue) key")
        }
      }
      value = dictionary
    } else if var unkeyedContainer = try? decoder.unkeyedContainer() {
      var array = [Any]()
      while !unkeyedContainer.isAtEnd {
        let container = try unkeyedContainer.decode(JsonContainer.self)
        array.append(container.value)
      }
      value = array
    } else if let singleValueContainer = try? decoder.singleValueContainer() {
      if let value = try? singleValueContainer.decode(Bool.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Int64.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Double.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(String.self) {
        self.value = value
      } else if singleValueContainer.decodeNil() {
        value = NSNull()
      } else {
        throw DecodingError.dataCorruptedError(in: singleValueContainer, debugDescription: "Unexpected value")
      }
    } else {
      let context = DecodingError.Context(codingPath: [], debugDescription: "Invalid data format for JSON")
      throw DecodingError.dataCorrupted(context)
    }
  }

  private struct Key: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
      self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
      self.init(stringValue: "\(intValue)")
      self.intValue = intValue
    }
  }
}

Tenga en cuenta que los tipos numéricos y booleanos están respaldados por NSNumber, de lo contrario, algo como esto no funcionará:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil
Alexey Kozhevnikov
fuente
¿Puedo decodificar solo las propiedades elegidas y dejar otras decodificadas automáticamente ya que tengo 15 propiedades que son suficientes para la decodificación automática y tal vez 3 que necesitan un manejo de decodificación personalizado?
Michał Ziobro
@ MichałZiobro ¿Desea decodificar parte de los datos en un objeto JSON y parte de ellos en variables de instancia independientes? ¿O está preguntando acerca de escribir un inicializador de decodificación parcial solo para parte del objeto (y no tiene nada en común con una estructura similar a JSON)? Que yo sepa, una respuesta a la primera pregunta es sí, a la segunda es no.
Alexey Kozhevnikov
Me gustaría tener solo algunas propiedades con decodificación personalizada y el resto con decodificación estándar predeterminada
Michał Ziobro
@ MichałZiobro Si te entiendo bien, no es posible. De todos modos, su pregunta no es relevante para la pregunta SO actual y vale la pena otra.
Alexey Kozhevnikov
0

decodificar usando decodificador y claves de codificación

public let dataToDecode: [String: AnyDecodable]

enum CodingKeys: CodingKey {
    case dataToDecode
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.dataToDecode = try container.decode(Dictionary<String, AnyDecodable>.self, forKey: .dataToDecode) 
}    
Ashim Dahal
fuente
-1
extension ViewController {

    func swiftyJson(){
        let url = URL(string: "https://itunes.apple.com/search?term=jack+johnson")
        //let url = URL(string: "http://makani.bitstaging.in/api/business/businesses_list")

        Alamofire.request(url!, method: .get, parameters: nil).responseJSON { response in
            var arrayIndexes = [IndexPath]()
            switch(response.result) {
            case .success(_):

                let data = response.result.value as! [String : Any]

                if let responseData =  Mapper<DataModel>().map(JSON: data) {
                    if responseData.results!.count > 0{
                        self.arrayExploreStylistList = []
                    }
                    for i in 0..<responseData.results!.count{
                        arrayIndexes.append(IndexPath(row: self.arrayExploreStylistList.count + i, section: 0))
                    }
                    self.arrayExploreStylistList.append(contentsOf: responseData.results!)

                    print(arrayIndexes.count)

                }

                //                    if let arrNew = data["results"] as? [[String : Any]]{
                //                        let jobData = Mapper<DataModel>().mapArray(JSONArray: arrNew)
                //                        print(jobData)
                //                        self.datamodel = jobData
                //                    }
                self.tblView.reloadData()
                break

            case .failure(_):
                print(response.result.error as Any)
                break

            }
        }

    }
}
contratar
fuente