Comment décoder une propriété avec le type de dictionnaire JSON dans le protocole décodable Swift 4

104

Disons que j'ai Customer type de données qui contient une metadatapropriété pouvant contenir n'importe quel dictionnaire JSON dans l'objet client

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 metadatapropriété peut être n'importe quel objet de carte JSON arbitraire.

Avant de pouvoir convertir la propriété à partir d'un JSON désérialisé à partir NSJSONDeserializationdu nouveau Decodableprotocole Swift 4 , je n'arrive toujours pas à trouver un moyen de le faire.

Est-ce que quelqu'un sait comment y parvenir dans Swift 4 avec le protocole décodable?

Pitiphong Phongpattranont
la source

Réponses:

90

En me inspirant de cet essentiel que j'ai trouvé, j'ai écrit des extensions pour UnkeyedDecodingContaineret 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)
    }
}
grande gueule
la source
Intéressant, je vais essayer l'essentiel et je mettrai à jour le résultat pour vous @loudmouth
Pitiphong Phongpattranont
@PitiphongPhongpattranont ce code a-t-il fonctionné pour vous?
loudmouth
1
@JonBrooks la dernière condition dans le dans UnkeyedDecodingContainer« s decode(_ 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é . La decodeméthode d'un UnkeyedDecodingContainer"saute" de l'élément du conteneur. Cela ne devrait pas provoquer de récursivité infinie.
loudmouth
1
@loudmouth il est possible d'avoir des valeurs nulles pour les clés de JSON: {"array": null}. Donc, vous guard 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'appeler decode.
chebur
2
J'ai trouvé une solution: au lieu d' } 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) {
Jon Brooks
23

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:

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

Ce type peut alors implémenter Codableet Equatable.

zoul
la source
13

Vous pouvez créer une structure de métadonnées qui confirme le Decodableprotocole et utiliser la JSONDecoderclasse pour créer un objet à partir de données en utilisant la méthode de décodage comme ci-dessous

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
la source
10
Non, je ne peux pas, car je ne connais pas la structure de la metadatavaleur. Cela peut être n'importe quel objet arbitraire.
Pitiphong Phongpattranont
Voulez-vous dire qu'il peut être de type tableau ou dictionnaire?
Suhit Patil
pouvez-vous donner un exemple ou ajouter plus d'explications sur la structure des métadonnées
Suhit Patil
2
La valeur de metadatapeut ê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.
Pitiphong Phongpattranont
une option possible serait d'utiliser tous les paramètres de la structure de métadonnées comme optionnels et de lister toutes les valeurs possibles dans la structure de métadonnées comme struct metadata {var user_id: String? préférence var: String? }
Suhit Patil
8

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:

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

Eh bien, voici ma solution:

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

Essayez-le en utilisant

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

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 AnyJSONValuestructure d'effacement de type et utilise ce type à la place de Any. Voici une implémentation.

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

Et voici comment l'utiliser lors du décodage

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

Le problème avec cette question est que nous devons appeler value.jsonValue as? Int. Nous devons attendre la Conditional Conformanceterre à 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

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

dans l'initialiseur.

C'était mon mal de rater ça en premier lieu.

Pitiphong Phongpattranont
la source
4
Pourrait publier le lien vers la question sur Apple Developer. Anyn'est pas conforme à, Decodabledonc je ne sais pas comment c'est la bonne réponse.
Reza Shirazian
@RezaShirazian C'est ce que je pensais en premier lieu. Mais il s'avère que Dictionary se conforme à Encodable lorsque ses clés sont conformes à Hashable et ne dépendent pas de ses valeurs. Vous pouvez ouvrir l'en-tête du dictionnaire et le voir par vous-même. extension Dictionary: Encodable where Key: Hashable extension Dictionary: Decodable where Key: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont
6
actuellement cela ne fonctionne pas. "Dictionary <String, Any> n'est pas conforme à Decodable parce que Any n'est pas conforme à Decodable"
mbuchetics
Il s'avère que cela fonctionne. Je l'utilise dans mon code. Vous devez comprendre qu'il n'y a aucun moyen d'exprimer l'exigence selon laquelle «la valeur du dictionnaire doit être conforme au protocole décodable afin de rendre le dictionnaire conforme au protocole décodable» maintenant. C'est la "Conformité Conditionnelle" qui n'est pas encore implémentée dans Swift 4 Je pense que c'est ok pour le moment car il y a beaucoup de limitations dans le Swift Type System (et les Generics). Donc, cela fonctionne pour le moment, mais lorsque le système de type Swift s'améliorera à l'avenir (en particulier lorsque la conformité conditionnelle est implémentée), cela ne devrait pas fonctionner.
Pitiphong Phongpattranont
3
Cela ne fonctionne pas pour moi à partir de Xcode 9 beta 5. Compile, mais explose au moment de l'exécution: Dictionary <String, Any> n'est pas conforme à Decodable car Any n'est pas conforme à Decodable.
zoul
6

Si vous utilisez SwiftyJSON pour analyser JSON, vous pouvez mettre à jour vers la version 4.1.0 qui prend en Codablecharge le protocole. Déclarez simplement metadata: JSONet vous êtes prêt.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}
allen huang
la source
Je ne sais pas pourquoi cette réponse a été rejetée. C'est totalement valable et résout le problème.
Leonid Usov
Cela semble être bon pour la migration de SwiftyJSON vers Decodable
Michał Ziobro
Cela ne résout pas comment analyser ensuite les métadonnées json qui étaient le problème d'origine.
llamacorn
1

Vous pourriez jeter un œil à 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
la source
Ooh, vraiment sympa. L'utiliser pour recevoir un JSON générique en tant que JToken, en ajoutant des valeurs et en retournant au serveur. Vraiment très bien. C'est un travail formidable que vous avez fait :)
Vitor Hugo Schwaab
1

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

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

    }
}

Usage:

  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 {

        }
    }

** J'ai utilisé optionnel pour être en sécurité lors de l'analyse, peut être modifié au besoin.

En savoir plus sur ce sujet

Minhazur
la source
1
Votre réponse est certainement la bonne pour Swift 4.1 et la première ligne de votre message est morte! En supposant que les données proviennent d'un service Web. vous pouvez modéliser des objets imbriqués simples, puis utiliser la syntaxe de points pour les saisir. Voir la réponse de suhit ci-dessous.
David H
1

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

pod 'DynamicCodable', '1.0'

Comment l'utiliser:

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
la source
0

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

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

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

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

Notez que les types numériques et booléens sont soutenus par NSNumber, sinon quelque chose comme ça ne fonctionnera pas:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil
Alexey Kozhevnikov
la source
Puis-je décoder uniquement les propriétés choisies et laisser les autres décodées automatiquement car j'ai 15 propriétés qui suffisent à l'autodécodage et peut-être 3 qui nécessitent une gestion de décodage personnalisée?
Michał Ziobro
@ MichałZiobro Voulez-vous qu'une partie des données soit décodée en objet JSON et une partie de celles-ci décodée en variables d'instance distinctes? Ou vous demandez d'écrire un initialiseur de décodage partiel juste pour une partie de l'objet (et il n'a rien de commun avec la structure JSON)? À ma connaissance, une réponse à la première question est oui, à la seconde est non.
Alexey Kozhevnikov
Je voudrais avoir seulement quelques propriétés avec un décodage personnalisé et le reste avec un décodage standard par défaut
Michał Ziobro
@ MichałZiobro Si je vous comprends bien, ce n'est pas possible. Quoi qu'il en soit, votre question n'est pas pertinente par rapport à la question SO actuelle et vaut une question distincte.
Alexey Kozhevnikov
0

décoder à l'aide du décodeur et des clés de codage

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
la source
-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
la source