Como decodificar uma propriedade com tipo de dicionário JSON no protocolo decodificável Swift 4

103

Digamos que eu tenha Customerum tipo de dados que contém uma metadatapropriedade que pode conter qualquer dicionário JSON no objeto do 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
  }
}

A metadatapropriedade pode ser qualquer objeto de mapa JSON arbitrário.

Antes que eu possa converter a propriedade de um JSON desserializado, NSJSONDeserializationmas com o novo Decodableprotocolo Swift 4 , ainda não consigo pensar em uma maneira de fazer isso.

Alguém sabe como conseguir isso no Swift 4 com protocolo decodificável?

Pitiphong Phongpattranont
fonte

Respostas:

89

Com alguma inspiração nessa essência que encontrei, escrevi algumas extensões para UnkeyedDecodingContainere KeyedDecodingContainer. Você pode encontrar um link para minha essência aqui . Usando este código, você agora pode decodificar qualquer um Array<Any>ou Dictionary<String, Any>com a sintaxe familiar:

let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)

ou

let array: [Any] = try container.decode([Any].self, forKey: key)

Editar: há uma ressalva que descobri que é decodificar uma série de dicionários. [[String: Any]]A sintaxe necessária é a seguinte. Você provavelmente desejará lançar um erro em vez de forçar o lançamento:

let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]

EDIT 2: Se você simplesmente deseja converter um arquivo inteiro em um dicionário, é melhor ficar com a API da JSONSerialization, pois não descobri uma maneira de estender o próprio JSONDecoder para decodificar diretamente um dicionário.

guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
  // appropriate error handling
  return
}

As extensões

// 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)
    }
}
falastrão
fonte
Interessante, vou tentar esta essência e atualizar o resultado para você @loudmouth
Pitiphong Phongpattranont
@PitiphongPhongpattranont esse código funcionou para você?
loudmouth
1
@JonBrooks a última condição no no UnkeyedDecodingContainer's decode(_ type: Array<Any>.Type) throws -> Array<Any>está verificando um nested matriz. Portanto, se você tiver uma estrutura de dados parecida com a seguinte: [true, 452.0, ["a", "b", "c"] ] Ela puxaria o ["a", "b", "c"]array aninhado . O decodemétodo de um UnkeyedDecodingContainer"retalho" do elemento do contêiner. Não deve causar recursão infinita.
loudmouth
1
@loudmouth é possível ter valores nulos para chaves em JSON: {"array": null}. Portanto, o seu guard contains(key)passará, mas travará algumas linhas depois ao tentar decodificar o valor nulo para a chave "array". Portanto, é melhor adicionar mais uma condição para verificar se o valor não é realmente nulo antes de chamar decode.
chebur
2
Eu encontrei uma solução: em vez de } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)tentar:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks de
23

Também brinquei com esse problema e finalmente escrevi uma biblioteca simples para trabalhar com tipos “JSON genéricos” . (Onde "genérico" significa "sem nenhuma estrutura conhecida de antemão".) O ponto principal é representar o JSON genérico com um tipo concreto:

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

Esse tipo pode então implementar Codablee Equatable.

zoul
fonte
13

Você pode criar uma estrutura de metadados que confirma o Decodableprotocolo e usa a JSONDecoderclasse para criar um objeto a partir dos dados usando o método de decodificação abaixo

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
fonte
10
Não posso, pois não conheço a estrutura do metadatavalor. Pode ser qualquer objeto arbitrário.
Pitiphong Phongpattranont
Quer dizer que pode ser do tipo Array ou Dicionário?
Suhit Patil
você pode dar um exemplo ou adicionar mais explicações sobre a estrutura de metadados
Suhit Patil
2
O valor de metadatapode ser qualquer objeto JSON. Portanto, pode ser um dicionário vazio ou qualquer dicionário. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferência: {shows_value: true, language: "en"}} etc.
Pitiphong Phongpattranont
uma opção possível seria usar todos os parâmetros na estrutura de metadados como opcionais e listar todos os valores possíveis na estrutura de metadados como metadados de estrutura {var user_id: String? preferência var: String? }
Suhit Patil
8

Eu vim com uma solução ligeiramente diferente.

Vamos supor que temos algo mais do que um simples [String: Any]para analisar se Any pode ser um array ou um dicionário aninhado ou um dicionário de arrays.

Algo assim:

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
    }
  ]
}
"""

Bem, esta é a minha solução:

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"))
    }
  }
}

Experimente usando

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

Quando encontrei a resposta antiga, testei apenas um caso de objeto JSON simples, mas não um caso vazio, o que causará uma exceção de tempo de execução como @slurmomatic e @zoul encontrados. Desculpe por este problema.

Então, tento outra maneira, tendo um protocolo JSONValue simples, implementar a AnyJSONValueestrutura de eliminação de tipo e usar esse tipo em vez de Any. Aqui está uma implementação.

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"))
        }
    }
}

E aqui está como usá-lo ao decodificar

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

O problema com esse problema é que devemos ligar value.jsonValue as? Int. Precisamos esperar até Conditional Conformancepousar em Swift, isso resolveria esse problema ou pelo menos ajudaria a melhorar.


[Resposta Antiga]

Eu posto esta questão no fórum de desenvolvedores da Apple e descobri que é muito fácil.

eu posso fazer

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

no inicializador.

Foi ruim para mim perder isso em primeiro lugar.

Pitiphong Phongpattranont
fonte
4
Poderia postar o link para a pergunta no Apple Developer. Anynão está em conformidade, Decodableentão não tenho certeza de como essa é a resposta correta.
Reza Shirazian
@RezaShirazian Foi o que pensei em primeiro lugar. Mas acontece que Dictionary está em conformidade com Encodable quando suas chaves estão em Hashable e não dependem de seus valores. Você pode abrir o cabeçalho do Dicionário e ver por si mesmo. Dicionário de extensão: Codificável onde Chave: Dicionário de extensão Hashable: Decodificável onde Chave: Fóruns.developer.apple.com
thread
6
atualmente isso não funciona. "Dicionário <String, Any> não está em conformidade com Decodable porque Any não está em conformidade com Decodable"
mbuchetics
Acontece que funciona. Estou usando no meu código. Você precisa entender que não há como expressar o requisito de que "O valor do dicionário deve estar em conformidade com o protocolo decodificável para que o dicionário esteja em conformidade com o protocolo decodificável" agora. Essa é a "conformidade condicional" que ainda não foi implementada no Swift 4. Acho que está tudo bem por enquanto, uma vez que existem muitas limitações no sistema de tipos Swift (e genéricos). Portanto, isso funciona por enquanto, mas quando o Swift Type System for aprimorado no futuro (especialmente quando a Conditional Conformance for implementada), não deverá funcionar.
Pitiphong Phongpattranont
3
Não funciona para mim a partir do Xcode 9 beta 5. Compila, mas explode em tempo de execução: Dictionary <String, Any> não está em conformidade com Decodable porque Any não está em conformidade com Decodable.
zoul
6

Se você usar o SwiftyJSON para analisar JSON, poderá atualizar para 4.1.0, que tem Codablesuporte de protocolo. Basta declarar metadata: JSONe está tudo pronto.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}
Allen Huang
fonte
Não sei por que essa resposta foi rejeitada. É totalmente válido e resolve o problema.
Leonid Usov
Parece ser bom para a migração do SwiftyJSON para o Decodable
Michał Ziobro
Isso não resolve como analisar os metadados json, que era o problema original.
llamacorn
1

Você pode dar uma olhada em 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
fonte
Ooh, muito bom. Usando-o para receber um JSON genérico como JToken, acrescentando alguns valores e retornando ao servidor. Realmente muito bom. Que trabalho incrível você fez :)
Vitor Hugo Schwaab
1

A maneira mais fácil e sugerida é criar um modelo separado para cada dicionário ou modelo que está em JSON .

Aqui está o que eu faço

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

        }
    }

** Usei o opcional para estar seguro durante a análise, pode ser alterado conforme necessário.

Leia mais sobre este assunto

minhazur
fonte
1
Sua resposta é a apropriada para o Swift 4.1 com certeza e a primeira linha de sua postagem está certa! Supondo que os dados sejam provenientes de um serviço da web. você pode modelar objetos aninhados simples e usar a sintaxe de pontos para capturar cada um. Veja a resposta de suhit abaixo.
David H de
1

Fiz um pod para facilitar o caminho a decodificação + codificação [String: Any], [Any]. E isso fornece codificar ou decodificar as propriedades opcionais, aqui https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

Como usá-lo:

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
fonte
0

Aqui está uma abordagem mais genérica (não apenas [String: Any], mas [Any]pode ser decodificada) e encapsulada (uma entidade separada é usada para isso) inspirada na resposta de @loudmouth.

O uso será semelhante a:

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

JsonContaineré uma entidade auxiliar que usamos para encapsular dados de decodificação JSON em objeto JSON (seja uma matriz ou dicionário) sem estender *DecodingContainer(portanto, não interfere em casos raros em que um objeto JSON não é referido [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
    }
  }
}

Observe que os tipos numéricos e booleanos são apoiados por NSNumber, caso contrário, algo como isto não funcionará:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil
Alexey Kozhevnikov
fonte
Posso decodificar apenas as propriedades escolhidas e deixar outras decodificadas automaticamente, pois tenho 15 propriedades que são suficientes para a autoDecoding e talvez 3 que precisam de algum tratamento de decodificação personalizado?
Michał Ziobro de
@ MichałZiobro Você quer parte dos dados decodificados no objeto JSON e parte deles decodificados em variáveis ​​de instância separadas? Ou você está perguntando sobre como escrever o inicializador de decodificação parcial apenas para parte do objeto (e não tem nada em comum com a estrutura do tipo JSON)? Pelo que sei, a resposta à primeira pergunta é sim, e à segunda não.
Alexey Kozhevnikov
Eu gostaria de ter apenas algumas propriedades com decodificação personalizada e o resto com decodificação padrão
Michał Ziobro
@ MichałZiobro Se bem o entendi, não é possível. De qualquer forma, sua pergunta não é relevante para a pergunta SO atual e vale uma pergunta separada.
Alexey Kozhevnikov
0

decodifique usando decodificador e chaves de codificação

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

            }
        }

    }
}
hiren
fonte