JSON parsing

Bonsoir à tous,

Je m'aperçois que je ne parse pas toutes les données JSON d'une URL, en effet il y a au début les clés next et previous :

 { "id":1, "next":"https://...&page=2", "previous":null, "results":[{...}] }

Comment récupérer les données de la page 2 et ainsi de suite ?

Merci.

Réponses

  • LarmeLarme Membre

    Quel est ton code actuellement de parsing ? Quel est ton modèle ?

    Ce que je ferais, en pseudo code :

    struct Page {
        let id: Int
        let next: String?
        let previous: String?
        let result: [Model]
    
        let nextURL: URL? {
             return URL(string: next)
        }
        let previous: URL? {
             return URL(string: previous)
        }
    }
    

    Tu fais de requêtes pour avoir la première page.
    Pour la seconde page, tu fais la même avec le nextURL.

    Si tu as besoin de concaténer rapidement tes résultats :

    let allModels = allPages.compactMap{ $0.result } 
    

    ou manuellement :

    var allModels = [Model]()
    for aPage in allPages {
        allModels.append(contentsOf: aPage.results)
    }
    
  • heliohelio Membre
    3 févr. modifié #3

    Merci, mais il peut y avoir plusieurs pages, je connais pas le nombre de pages à l'avance.

    Voici mon code actuellement :

    modele :

    struct Produit: Codable {
       let id: Int
       let next, previous: String?
       let results: [Result]
    
       var nextURL: String? {
           return next
       }
       var previousURL: String? {
           return previous
       }
    

    }

    fonction Parser, la solution serait peut être que ma fonction soit récursive mais avec completion je ne sais pas comment faire !

     func Parser(urlString:String, _ completion:@escaping (_ qty:[Int], _ name:[String], _ id:[String], _ img:[String]) -> ()) {
    
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
    
        let urlPath = "https://..."
        let url = URL(string: urlPath)
        let urlRequest = URLRequest(url: url!)
        let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }
            guard error == nil else {
                print("error calling GET on /posts/1")
                print(error as Any)
                return
            }
    
            do {
    
                let decoder = JSONDecoder()
                let model = try decoder.decode(Produit.self, from:
                    responseData) //Decode JSON Response Data
    
    
                for j in 0...(model.results.count-1) {
    
                    let num = model.results[j].part.Num
                    self.id.append(num)
    
                    let qty = model.results[j].quantity
                    self.qty.append(qty)
    
                    let name = model.results[j].part.name
                    self.name.append(name)
    
                    let img = model.results[j].part.ImgURL
                    self.img.append(img)
    
    
                    if let nextPage = model.next {
    
    
                        if !nextPage.isEmpty {
                            print("nextPage no empty")
                            print(nextPage)
                            // self.Parser(urlString: nextPage, completion)
    
                        }
    
                    }
    
                }
    
    
            } catch let parsingError {
                print("Error", parsingError)
            }
    
    
            completion(self.qty, self.name, self.id, self.img)
    
     })
     task.resume()
    

    Merci.

  • LarmeLarme Membre
    Oulà !
    Quand je vois la boucle for j, à bannir !
    Je reviendrais dessus si personne ne l’a fait quand je serais rentré.
    Mais explique-moi pourquoi tu fais ça ? Pourquoi tu as un array pour les names, un pour l’img, etc ?
  • heliohelio Membre

    Pour les afficher dans un TableView ou CollectionView de cette façon :
    cell.labelName.text = name[indexPath.row]
    etc...

    merci.

  • LarmeLarme Membre

    C'est bien ce que je pensais, alors non.

    Il te faut un seul array de Results (si les pages ne t'intéressent pas): var results: [Results]
    Ces valeurs n'ont de sens qu'ensemble, si tu mélanges qty[1] et name[0], ça n'a plus de sens, on est d'accord.

    Puis, tu fais :

    let result = results[indexPath.row]
    cell.labelName.text = result.name
    cell.labelQty.text = result.qty ou String(result.qty)
    

    Pourquoi ?
    Imagines que demain, tu veux faire un filtre, un sort différent, supprimer un élément, rajouter un élément, etc.
    Tu vas modifier les 4? La synchro n'est pas forcément évidente à faire non plus.
    Le cas le plus simple pour montrer la complexité, est le sort ou le filter. Essaye d'en faire un, tu vas voir.

  • heliohelio Membre

    @Larme a dit :

    let result = results[indexPath.row]
    cell.labelName.text = result.name
    cell.labelQty.text = result.qty ou String(result.qty)
    

    Ah oui exact plus simple comme ça effectivement ! merci.

    Et pour mon problème de parser les url qui sont dans "next" tu aurais une idée ?
    comme je disais dans un précédent message, je pensais faire une fonctionne récursive ?

  • LarmeLarme Membre
    4 févr. modifié #8

    Je n'ai pas testé, mais en pseudo code, je pense que cela devrait fonctionner.

    func download(nextPages currentPages: [Page], completion: ((Pages) -> Void) {
        guard let lastPage = currentPages.last, let nextURL = lastPage.nextURL else { completion(currentPages); return }
        Request.download(nextURL, completion { (response, error) in
            guard error != nil else { completion(currentPages); return } 
            let page: Page = Parse(response)
            let allPages = currentPages + page
            download(nextPages: allPages, completion: completion)
        }
    }
    
  • heliohelio Membre
    9 févr. modifié #9

    Merci Larme, cela fonctionne désormais j'ai bien toutes les données.
    En revanche juste un petit problème : lorsque la premiere URL est parsée, la CollectionView se charge comme si tout est terminé et ensuite l'écran clignote le temps que les pages restantes chargent les données suivantes.
    Comment améliorer ceci ?

    Du côté de ma CollectionViewController, j'ai ceci :

                backgroundQueue.async(execute: {
    
                let p = Part.init()
    
                let urlPath = "https://..."
    
                p.Parser (urlString: urlPath) { (result) -> () in
    
                    self.xResult = result
    
                    DispatchQueue.main.async(execute: { () -> Void in
    
                        self.xNbP = self.xResult.count
                        self.collectionView?.reloadData()
                        self.activityIndicatorView.stopAnimating()
    
    
                    })
    
    
    
                }
    
Connectez-vous ou Inscrivez-vous pour répondre.