Come decodificare una proprietà con il tipo di dizionario JSON nel protocollo decodificabile Swift 4


103

Diciamo che ho un Customertipo di dati che contiene una metadataproprietà che può contenere qualsiasi dizionario JSON nell'oggetto cliente

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

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

La metadataproprietà può essere qualsiasi oggetto mappa JSON arbitrario.

Prima di poter eseguire il cast della proprietà da un JSON deserializzato da NSJSONDeserializationma con il nuovo Decodableprotocollo Swift 4 , non riesco ancora a pensare a un modo per farlo.

Qualcuno sa come ottenere questo risultato in Swift 4 con il protocollo Decodable?

Risposte:


89

Con qualche ispirazione da questa sintesi che ho trovato, ho scritto alcune estensioni per UnkeyedDecodingContainere KeyedDecodingContainer. Puoi trovare un link alla mia sintesi qui . Usando questo codice ora puoi decodificare qualsiasi Array<Any>o Dictionary<String, Any>con la sintassi familiare:

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

o

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

Modifica: c'è un avvertimento che ho trovato che sta decodificando un array di dizionari [[String: Any]]La sintassi richiesta è la seguente. Probabilmente vorrai lanciare un errore invece di forzare il casting:

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

EDIT 2: Se vuoi semplicemente convertire un intero file in un dizionario, faresti meglio a restare fedele a api da JSONSerialization poiché non ho trovato un modo per estendere JSONDecoder stesso per decodificare direttamente un dizionario.

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

Le estensioni

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

Interessante, proverò questa sintesi e aggiornerò il risultato per te @loudmouth
Pitiphong Phongpattranont

@PitiphongPhongpattranont ha funzionato questo codice per te?
chiacchierone

1
@JonBrooks l'ultima condizione della a UnkeyedDecodingContainer's decode(_ type: Array<Any>.Type) throws -> Array<Any>è il controllo per un nidificata matrice. Quindi, se si dispone di una struttura dati simile alla seguente: [true, 452.0, ["a", "b", "c"] ] verrà estratto l' ["a", "b", "c"]array nidificato . Il decodemetodo di UnkeyedDecodingContainer"espulsione" dell'elemento dal contenitore. Non dovrebbe causare una ricorsione infinita.
chiacchierone

1
@loudmouth è possibile avere valori nulli per le chiavi in JSON: {"array": null}. Quindi guard contains(key)si passerà ma si bloccherà poche righe dopo quando si tenta di decodificare il valore nullo per la chiave "array". Quindi è meglio aggiungere un'altra condizione per verificare se il valore non è effettivamente nullo prima di chiamare decode.
chebur

2
Ho trovato una soluzione: invece di } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)provare:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks

23

Ho anche giocato con questo problema e alla fine ho scritto una semplice libreria per lavorare con i tipi "JSON generici" . (Dove "generico" significa "senza struttura nota in anticipo".) Il punto principale è rappresentare il JSON generico 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
}

Questo tipo può quindi implementare Codablee Equatable.


13

È possibile creare strutture di metadati che confermano il Decodableprotocollo e utilizzare la JSONDecoderclasse per creare oggetti dai dati utilizzando il metodo di decodifica come di seguito

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "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)
}

10
No, non posso, poiché non conosco la struttura del metadatavalore. Può essere qualsiasi oggetto arbitrario.
Pitiphong Phongpattranont

Vuoi dire che può essere di tipo Array o Dictionary?
Suhit Patil

puoi fare un esempio o aggiungere ulteriori spiegazioni sulla struttura dei metadati
Suhit Patil

2
Il valore di metadatapuò essere qualsiasi oggetto JSON. Quindi può essere dizionario vuoto o qualsiasi dizionario. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferenza: {shows_value: true, language: "en"}} ecc.
Pitiphong Phongpattranont

una possibile opzione sarebbe quella di utilizzare tutti i parametri nella struttura dei metadati come optional ed elencare tutti i possibili valori nella struttura dei metadati come struct metadata {var user_id: String? preferenza var: String? }
Suhit Patil

8

Sono arrivato con una soluzione leggermente diversa.

Supponiamo di avere qualcosa di più di un semplice [String: Any]da analizzare se Any potrebbe essere un array o un dizionario annidato o un dizionario di array.

Qualcosa come questo:

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

Bene, questa è la mia soluzione:

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

Provalo usando

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

6

Quando ho trovato la vecchia risposta, ho testato solo un semplice caso di oggetto JSON ma non uno vuoto che causerà un'eccezione di runtime come @slurmomatic e @zoul trovati. Ci scusiamo per questo problema.

Quindi provo un altro modo avendo un semplice protocollo JSONValue, implemento la AnyJSONValuestruttura di cancellazione del tipo e utilizzo quel tipo invece di Any. Ecco un'implementazione.

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

Ed ecco come usarlo durante la decodifica

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

Il problema con questo problema è che dobbiamo chiamare value.jsonValue as? Int. Dobbiamo aspettare fino all'arrivo Conditional Conformancea Swift, questo risolverebbe questo problema o almeno lo aiuterebbe a essere migliore.


[Vecchia risposta]

Ho posto questa domanda sul forum degli sviluppatori Apple e risulta che è molto semplice.

posso fare

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

nell'inizializzatore.

È stato un problema per me mancare quello in primo luogo.


4
Potrebbe pubblicare il collegamento alla domanda su Apple Developer. Anynon è conforme a, Decodablequindi non sono sicuro di come questa sia la risposta corretta.
Reza Shirazian

@RezaShirazian Questo è quello che ho pensato in primo luogo. Ma risulta che Dictionary è conforme a Encodable quando le sue chiavi sono conformi a Hashable e non dipendono dai suoi valori. Puoi aprire l'intestazione del dizionario e vederlo da solo. Dizionario di estensione: codificabile dove Chiave: hashable Dizionario di estensione: decodificabile dove chiave: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
attualmente questo non funziona. "Dictionary <String, Any> non è conforme a Decodable perché Any non è conforme a Decodable"
mbuchetics

Si scopre che funziona. Lo sto usando nel mio codice. Devi capire che non c'è modo di esprimere il requisito che "il valore del dizionario deve essere conforme al protocollo decodificabile per rendere il dizionario conforme al protocollo decodificabile". Questa è la "Conformità condizionale" che non è ancora implementata in Swift 4. Penso che per ora vada bene dato che ci sono molte limitazioni nello Swift Type System (e nei generici). Quindi per ora funziona, ma quando lo Swift Type System migliorerà in futuro (specialmente quando verrà implementata la conformità condizionale), non dovrebbe funzionare.
Pitiphong Phongpattranont

3
Non funziona per me a partire da Xcode 9 beta 5. Compila, ma esplode in fase di esecuzione: Dictionary <String, Any> non è conforme a Decodable perché Any non è conforme a Decodable.
Zoul

6

Se utilizzi SwiftyJSON per analizzare JSON, puoi eseguire l'aggiornamento a 4.1.0 che Codablesupporta il protocollo. Dichiara metadata: JSONe sei pronto.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}

Non so perché questa risposta sia stata sottovalutata. È totalmente valido e risolve il problema.
Leonid Usov

Sembra essere positivo per la migrazione da SwiftyJSON a Decodable
Michał Ziobro

Questo non risolve come analizzare poi il json dei metadati che era il problema originale.
llamacorn

1

Potresti dare un'occhiata 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)!)

Ooh, davvero carino. Usandolo per ricevere un JSON generico come JToken, aggiungendo alcuni valori e tornando al server. Davvero molto bravo. È un lavoro fantastico che hai fatto :)
Vitor Hugo Schwaab

1

Il modo più semplice e suggerito è creare un modello separato per ogni dizionario o modello che si trova in JSON .

Ecco cosa faccio

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

    }
}

Utilizzo:

  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 {

        }
    }

** Ho usato facoltativo per essere sicuro durante l'analisi, può essere modificato secondo necessità.

Maggiori informazioni su questo argomento


1
La tua risposta è sicuramente quella appropriata per Swift 4.1 e la prima riga del tuo post è morta! Supponendo che i dati provengano da un servizio web. puoi modellare oggetti nidificati semplici, quindi utilizzare la sintassi dei punti per prenderli. Vedi la risposta di suhit di seguito.
David H

1

Ho fatto un contenitore per facilitare il modo codificante la decodifica + [String: Any], [Any]. E questo fornisce codificare o decodificare le proprietà opzionali, qui https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

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

0

Ecco un approccio più generico (non solo [String: Any], ma [Any]decodificabile) e incapsulato (per questo viene utilizzata un'entità separata) ispirato alla risposta di @loudmouth.

Usandolo sembrerà:

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è un'entità helper che utilizziamo per eseguire il wrapping dei dati JSON di decodifica in un oggetto JSON (array o dizionario) senza estenderli *DecodingContainer(quindi non interferirà con i rari casi in cui un oggetto JSON non è indicato con [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
    }
  }
}

Nota che i tipi numerici e booleani sono supportati da NSNumber, altrimenti qualcosa del genere non funzionerà:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil

Posso decodificare solo le proprietà scelte e lasciare altre decodificate automaticamente dato che ho 15 proprietà che sono sufficienti per l'autoDecoding e forse 3 che richiedono una gestione della decodifica personalizzata?
Michał Ziobro

@ MichałZiobro Vuoi che parte dei dati venga decodificata in un oggetto JSON e parte di esso decodificata in variabili di istanza separate? O stai chiedendo di scrivere un inizializzatore di decodifica parziale solo per una parte dell'oggetto (e non ha nulla in comune con la struttura simile a JSON)? Per quanto ne so, una risposta alla prima domanda è sì, alla seconda no.
Alexey Kozhevnikov il

Vorrei avere solo alcune proprietà con decodifica personalizzata e il resto con decodifica predefinita standard
Michał Ziobro

@ MichałZiobro Se ho capito bene non è possibile. Ad ogni modo, la tua domanda non è rilevante per l'attuale domanda SO e merita una separata.
Alexey Kozhevnikov il

0

decodificare utilizzando decoder e chiavi di codifica

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

-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

            }
        }

    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.