web-dev-qa-db-fra.com

Mappage d'un objet JSON sur une classe/structure Swift

Je dois "répliquer" une entrée renvoyée par un service API Web distant en JSON. Cela ressemble à ceci:

{
  "field1": "some_id",
  "entity_name" = "Entity1"
  "field2": "some name",
  "details1": [{
    "field1": 11,
    "field2": "some value",
    "data": {
      "key1": "value1",
      "key2": "value2",
      "key3": "value3",
      // any other, unknown at compile time keys
    }
  }],
  "details2": {
    "field1": 13,
    "field2": "some value2"
  }
}

Voici ma tentative:

struct Entity1 {
  struct Details1 {
    let field1: UInt32
    let field2: String
    let data: [String: String]
  }

  struct Details2 {
    let field1: UInt32
    let field2: String
  }

  let field1: String
  static let entityName = "Entity1"
  let field2: String
  let details1: [Details1]
  let details2: Details2 
}
  1. Est-ce une bonne idée d’utiliser des structures au lieu de classes pour un tel objectif Comme mien? 
  2. Puis-je quand même définir une structure ou une classe imbriquée, disons Détails1 et créer une variable de celle-ci en même temps? 

Comme ça:

//doesn't compile 
struct Entity1 {
  let details1: [Details1 { 
  let field1: UInt32
  let field2: String
  let data: [String: String]
}]
9
user266003

Vous pouvez utiliser l'une quelconque des bonnes bibliothèques disponibles ci-dessous pour gérer le mappage de JSON à Object dans Swift:

Chacun a un bon tutoriel pour les débutants. 

En ce qui concerne le thème struct ou class, vous pouvez considérer le texte suivant tiré de Le langage de programmation Swift documentation:

Les instances de structure sont toujours passées par valeur et les instances de classe Sont toujours passées par référence. Cela signifie qu'ils sont Adaptés à différents types de tâches. Lorsque vous considérez les constructions de données Et les fonctionnalités dont vous avez besoin pour un projet, décidez Si chaque construction de données doit être définie en tant que classe ou en tant que structure.

En règle générale, envisagez de créer une structure lorsqu'un ou plusieurs De ces conditions s'appliquent:

  • L’objectif principal de la structure est d’encapsuler quelques valeurs de données relativement simples.
  • Il est raisonnable de s’attendre à ce que les valeurs encapsulées soient copiées plutôt que référencées lorsque vous affectez ou transmettez une instance De cette structure.
  • Toutes les propriétés stockées par la structure sont elles-mêmes des types de valeur, qui devraient également être copiées plutôt que référencées.
  • La structure n'a pas besoin d'hériter des propriétés ou du comportement d'un autre type existant.

Voici des exemples de bons candidats pour les structures:

  • La taille d'une forme géométrique, encapsulant peut-être une propriété width et une propriété height, toutes deux de type Double.
  • Un moyen de faire référence à des plages dans une série, encapsulant éventuellement une propriété de départ et une propriété de longueur, de type Int.
  • Un point dans un système de coordonnées 3D, encapsulant éventuellement des propriétés x, y et z, chacune de type Double.

Dans tous les autres cas, définissez une classe et créez des instances de cette classe À gérer et à transmettre par référence. En pratique, cela signifie que La plupart des structures de données personnalisées doivent être des classes et non des structures.

J'espère que cela vous aidera.

16
Victor Sigler

HandyJSON est exactement ce dont vous avez besoin. Voir exemple de code:

 struct Animal: HandyJSON {
 nom de la variable: String? 
 var id: Chaîne? 
 var num: Int? 
} 
 
 let jsonString = "{\" name\": \" cat\", \" id\": \" 12345\", \" num\": 180}" 
 
 si animal = JSONDeserializer.deserializeFrom (json: jsonString) {
 empreinte (animal) 
} 

https://github.com/alibaba/handyjson

5
henshao

JSON Mapping dans Swift

Détails

  • xCode 9.1, Swift 4 
  • xCode 8.2.1, Swift 3

Tâche:

Obtenez les résultats de la recherche iTunes avec API de recherche iTunes avec une simple requête https://iTunes.Apple.com/search?term=jack+johnson

Requérant des pods:

  1. Alamofire - chargement des données
  2. ObjectMapper - mapping

Podfile

platform :ios, '8.0'
use_frameworks!

target 'stackoverflow-29749652' do
pod 'Alamofire'
pod 'ObjectMapper'
end

Code

classe NetworkManager

import Foundation
import Alamofire

// data loader
class NetworkManager {

class func request(string: String, block: @escaping ([String: Any]?)->()) {
        Alamofire.request(string).responseJSON { response in

            // print(response.request)  // original URL request
            // print(response.response) // HTTP URL response
            // print(response.data)     // server data
            // print(response.result)   // result of response serialization

            if let result = response.result.value as? [String: Any] {
                block(result)
            } else {
                block(nil)
            }
        }
    }
}

classe ItunceItem (mappage)

import Foundation
import ObjectMapper

class ItunceItem: Mappable {
    var wrapperType: String?
    var artistId:Int?
    var trackName:String?
    var trackPrice: Double?
    var currency:String?

    required convenience init?(map: Map) {
        self.init()
    }

    private func unwrapedDescription(value: Any?) -> String {
        if let value = value {
            return "\(value)"
        }
        return "[no data]"
    }

    var description: String {
        var _result = " trackName: \(unwrapedDescription(value: trackName))\n"
        _result += " artistId: \(unwrapedDescription(value: artistId))\n"
        _result += " wrapperType: \(unwrapedDescription(value: wrapperType))\n"
        _result += " price: "

        if let trackPrice = self.trackPrice, let currency = self.currency {
            _result += "\(trackPrice) \(currency)\n"
        } else {
             _result += "[no data]"
        }
        return _result
    }

    func mapping(map: Map) {
        trackName           <- map["trackName"]
        artistId            <- map["artistId"]
        wrapperType         <- map["wrapperType"]
        trackPrice          <- map["trackPrice"]
        currency            <- map["currency"]
    }
}

Usage

NetworkManager.request(string: "https://iTunes.Apple.com/search?term=jack+johnson") { json in
    if let json = json, let array = json["results"] as? [[String:Any]] {
        for item in array {
            if let itunceItem = ItunceItem(JSON: item) {
                print("----------------------------------------------")
                print(itunceItem.description)
            }
        }
    }
}

Résultat

 enter image description here

Analyse de fichiers json pratique (mécanique)

http://json.parser.online.fr

 enter image description here

3
Vasily Bodnarchuk

Jetez un coup d'œil à cette superbe bibliothèque qui répond parfaitement à vos besoins, Argo sur GitHub .

Dans votre cas, un struct est ok. Vous pouvez en savoir plus sur le choix entre une structure et une classe ici .

0
sweepy_

vous pouvez utiliser SwiftyJson et let json = JSONValue(dataFromNetworking) if let userName = json[0]["user"]["name"].string{ //Now you got your value }

0
bLacK hoLE

Vous pouvez aller avec cette extension pour Alamofire https://github.com/sua8051/AlamofireMapper

Déclarez une classe ou une structure:

class UserResponse: Decodable {
    var page: Int!
    var per_page: Int!
    var total: Int!
    var total_pages: Int!

    var data: [User]?
}

class User: Decodable {
    var id: Double!
    var first_name: String!
    var last_name: String!
    var avatar: String!
}

Utilisation:

import Alamofire
import AlamofireMapper

let url1 = "https://raw.githubusercontent.com/sua8051/AlamofireMapper/master/user1.json"
        Alamofire.request(url1, method: .get
            , parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject { (response: DataResponse<UserResponse>) in
                switch response.result {
                case let .success(data):
                    dump(data)
                case let .failure(error):
                    dump(error)
                }
        }
0
Sua Le