[Swift] Recupérer des données en JSON

ReiseikunReiseikun Membre
mars 2016 modifié dans API UIKit #1

Bonjour,


Je vous explique mon problème 


Je dois recupérer des données JSON du type : 


{"result":"OK","1":{"pseudo":"Reiseikun","lvl":"100","classe":"ranger"},"2":{"pseudo":"kunReisi","lvl":"99","classe":"guerrier"}}


J'aimerai pouvoir récupérer le pseudo ainsi que le lvl de chaque joueurs et les stocker dans une variable pour les utiliser dans une autre vue, quelque soit le nombre de joueurs existant.

Sachant quand dans la base de données la clé des joueurs est auto incrémenté, ce qui donnera une suite de nombres.


Je suis partie sur cette idée : 

  

 



let num = ["1","2","3"]

for number in num {
if let joueur = jsonData.valueForkey(number) as ? [String: AnyObject]{
var pseudo = joueur["pseudo"]
var lvl = joueur["lvl"]

maVariableIneffacable.setObject(pseudo, forkey: "PSEUDO")
maVariableIneffacable.setObject(lvl, forkey: "LVL")

}
}

 

est-ce que vous auriez une idée plus brillante que la mienne 


PS: jsonData est ma variable qui récupère les données JSON et fonctionne parfaitement 


Vous remerciant par avance pour vos retours


Réponses

  • samirsamir Membre

    Si tu as la main sur le JSON généré, il vaut mieux le modifier, pour moi ça n'as pas de sens cette structure ou bien j'ai mal compris les choses. Je ne comprends pas le sens des clés 1, 2,...


     


    Pourquoi pas un truc de genre: renvoyer les pseudos des jours dont le résultat est OK. ?



    [{"pseudo": "Reiseikun",
    "lvl": "100"
    }, {
    "pseudo": "kunReisi",
    "lvl": "99"
    }]
  • Non je n'ai pas la main sur le JSON


    On doit se connecter avant d'afficher ces informations.

    Donc le "result":"ok" indique que la connexion a bien été effectué 


    En ce qui concerne les clés 1,2,.. 

    c'est auto incrémenté donc a chaque ajout d'un personnage pour l'utilisateur la clé portera comme nom le nombre suivant, avec les informations du personnage ajouté ( pseudo,lvl)


  • samirsamir Membre
    mars 2016 modifié #4

    Bon ok... mais je pense que la structure de JSON est a re-réfléchir :)


     


    personnelement je ferais un truc de genre:



    class Joueur {

    let level: Int
    let pseudo: String

    init(level: Int, pseudo: String) {
    self.level = level
    self.pseudo = pseudo
    }
    }

    func parseJoeurs(data: NSDictionary) -> [Joueur] {
    return data.allValues.flatMap({ (obj: AnyObject) -> Joueur? in

    guard let dic = obj as? NSDictionary, let lvl = dic["lvl"] as? Int, let pseudo = dic["pseudo"] as? String
    else { return nil}
    return Joueur(level: lvl, pseudo: pseudo)
    })
    }

    Essaie d'appeler cette fonction en lui passant le json que tu as récupéré (jsonData). Un truc comme ça:



    let joueurs = parseJoueur(jsonData)
    let j1 = joueurs[0] // Attention à  cet accès il faudra tester la longueur de la liste joueurs.

    (Attention je n'ai pas testé le code. )


  • AliGatorAliGator Membre, Modérateur
    mars 2016 modifié #5
    Je suis d'accord que la structure du JSON est très mal foutue.

    En général dans une API classique on a plutôt une clé "success" qui vaut le booléen "true" ou "false" (et pas une clé "result" et une string "OK"), et une autre clé "results" qui est de type tableau, et contient les résultats (tes joueurs). Alors que là  les clés qu'il y a à  la racine du JSON mélangent des clés d'information ("result": "OK") et des clés identifiants de joueurs ("1", "2", ...), du coup on est obligé de vérifier d'avantage si on parse un joueur ou un truc qui n'a rien à  voir...

    Après, même si tu n'a pas la possibilité de changer le JSON car tu n'as pas la main sur le serveur / l'API que tu attaques, ce qui me choque dans ta proposition de code c'est que :
    - tu utilises num avec les valeurs 1,2,3 en dur pour l'instant... mais comment tu sais quelles seront les valeurs à  utiliser ?
    - tu dis que ces chiffres sont auto-incrémentés (ce sont donc certainement des IDs de joueurs, classique), mais ça ne veut pas forcément dire qu'ils vont se suivre. Si un joueur est banni du système et supprimé de la base de données, cela peut générer un trou dans la suite d'IDs, donc ne pas se baser sur cette propriété, l'ID qui vient après le joueur 3 peut tout à  fait être 5 car le 4 qui a existé a un moment n'existe plus.

    A mon avis le mieux est de directement itérer sur les couples clé/valeur, plutôt que d'avoir un tableau (arbitraire) de clés.

    De plus, ce côté "maVariableIneffacable" ainsi que cet usage de setObject:forKey me parait bizarre également. Et comme samir, je préconiserais plutôt de créer un type "Player" (sauf que moi j'en ferai plutôt une struct) pour manipuler de façon plus propre tes objets modèles "Player"

    ---

    Du coup, +1 pour le code de Samir dans l'ensemble, qui est une meilleure approche. J'y ferai quand même quelques petites retouches, comme par exemple :
    • utiliser des struct plutôt que class me semble plus adapté ici
    • le fait de ne parser que les allValues plutôt que les paires (clé, valeur) me semble dommage pour le jour où tu voudrais utiliser cet ID
    • le fait que "dic["lvl"] as? Int" sera toujours faux d'après la tronche de ton JSON foireux, car ce sont toujours des chaà®nes (même si ce sont des chaà®nes représentant un entier, " "123" as? Int " va échouer en Swift, car ce n'est pas directement du type Int donc c'est normal que ça soit faux). Pour interpréter une String en entier il faut utiliser Int(maString)
    Du coup ça pourrait donner un truc comme ça (non testé)
    struct Player {
    let playerID: Int
    let pseudo: String
    let level: Int
    }

    func parsePlayers(jsonData: [String: AnyObject]) -> [Player] {
    return jsonData.flatMap { (key: String, value: AnyObject) -> Player? in
    guard
    let playerID = Int(key)
    let dic = value as? [String: String],
    let lvlString = dic["lvl"], lvl = Int(lvlString),
    let pseudo = dic["pseudo"]
    else { return nil }
    return Player(playerID: playerID, pseudo: pseudo, level: lvl)
    }
    }
  • J'y peux rien concernant la structure du json..


    Merci pour vos solutions, j'arrive bien a récupérer les informations dont j'ai besoin.

    Vous préconisez quoi comme méthode pour afficher dans une nouvelles vue toutes les informations récupérées ?


  • DrakenDraken Membre
    mars 2016 modifié #7

    Une jolie image de fond avec une texture de parchemin, dans un UIImageView.


     


    Choisir une police de caractère iOS d'allure médiéval (BradleyHandITCTT-Bold, Chalkboard, Chalkduster, ou encore Papyrus).


    Le site iosfonts.com permet de visualiser toutes les polices disponibles sous iOS.


     


    Tu peux utiliser des labels pour afficher tes valeurs. Et ramer un certain temps, pour apprendre à  réaliser des interfaces s'adaptant à  toutes les tailles d'écrans.


  • Merci pour tes conseils pour la présentation.


    Mais je voulais plutôt dire, s'il fallait que j'utilise des labels ou bien un autre élément de xocde. 
    Et de quelle manière je devais m'y prendre pour passer en paramètre les information récupéré pour pouvoir les utiliser dans une nouvelle vue


  • DrakenDraken Membre
    avril 2016 modifié #9

    Euh .. difficile de répondre. Il y a plusieurs manières de le faire. Tout dépend de tes besoins.


     


    S'il peut y avoir beaucoup de noms, le plus simple c'est d'utiliser une TableView, pour scroller facilement sur la liste.


     


    Voici une série de vidéos (en français) expliquant comment utiliser les TableView : 


    http://pagesperso.lip6.fr/Fabrice.Kordon/5I452-2014/semaine-08.php


     


    Cette autre vidéo, du même cours, montre comment récupérer un fichier XML et l'afficher sur l'écran. C'est presque la même chose que ton problème :


     


    http://pagesperso.lip6.fr/Fabrice.Kordon/5I452-2014/index.php?C=10&S=11


     


    EDIT : La manière de transmettre des informations entre plusieurs vues a été expliqué dans ce topic :


     


    http://forum.cocoacafe.fr/topic/13849-résolu-transmission-de-variables-au-sein-des-pages-dun-uipagviewcontroller/page-2#entry133162


  • Ok Merci bien !


    J'étais parti sur l'optique d'utiliser "maVariableIneffacable" ( qui ne semble pas être une bonne méthode), pour l'afficher dans un label ou autre, mais je ne peux afficher que la dernière valeur récupéré et non la liste complète 


  • ReiseikunReiseikun Membre
    mai 2016 modifié #11

    Bonjour,
     Je reviens vers vous avec un nouveau problème en JSON ..

    Je possède le JSON ci-dessous : 

    {"list_joueurs":[{"id":"404abc","guilde":"Symphonie","pseudo":"ReiseiKun","lvl":"56.07"},
    {"id":"403xyz","guilde":"Harmony","pseudo":"KunRei","lvl":"54.00"}],"succes_joueurs":{"404abc":[{"date":"2016-05-11 15:29:00","nom_succes":"Up lvl 56 !","id_succes":"405"},{"date":"2016-05-10 12:00:00","nom_succes":"Up lvl 55!"}],"403xyz":[{"date":"2016-05-11 13:00:00","nom_succes":"Up lvl 54 !"}]}}

    J'aimerai a l'aide de ce code, premièrement récupérer les données JSON et  pouvoir afficher la liste des joueurs de ce compte avec les informations suivante : Guilde pseudo et lvl. 

    Puis après avoir sélectionné un joueur, selon son id récupérer le reste des données JSON et afficher les succès qui lui sont lié ( date et nom du succès)

    Ca donnerai quelque-chose de ce genre   

         
    SheYX5N.png


  • Pourquoi est-ce que tu fait appel plusieurs fois à  un fichier JSON ? Ne serait-il pas plus simple de lire le JSON une seule fois, pour charger les données en mémoire dans un tableau ou une collection ?

  • ReiseikunReiseikun Membre
    mai 2016 modifié #13

     Oui c'est ce que je souhaite faire et afficher les informations des joueurs qui sont propre à  leurs id.

    Et j'aimerai savoir comment m'y prendre 


  • macphimacphi Membre
    juin 2016 modifié #14

    Bonjour 


     


    Je dois récupérer ces données : 


    {


    "results":

    {

    "sunrise":"7:27:02 AM",

    "sunset":"5:05:55 PM",

    "solar_noon":"12:16:28 PM",

    "day_length":"9:38:53",

    "civil_twilight_begin":"6:58:14 AM",

    "civil_twilight_end":"5:34:43 PM",

    "nautical_twilight_begin":"6:25:47 AM",

    "nautical_twilight_end":"6:07:10 PM",

    "astronomical_twilight_begin":"5:54:14 AM",

    "astronomical_twilight_end":"6:38:43 PM"

    },

    "status":"OK"

    }


     


    Je rame car contrairement à  d'habitude il n'y a pas de tableau...


    Et c'est du one shot !


     


    Si je m'inspire de la solution d'AliGator avec la flatMap, je suis sur la bonne voie ?


     


    Pour l'anecdote, dans la réponse à  ma dernière question, il y avait aussi la flatMap.


    C'est pas simple mais je persévère pour tuer le moins possible de poneys...


    ::)


  • Salut,


    Je vais te proposer quelquechose mais je ne penses pas que ça soit la meilleure des solutions ...


    Admettons que tu ais déjà  ta variable qui te permet de récupérer ton JSON, que nous appellerons "jsonData"


    Ca donnera quelque chose du genre :



    if let meteo = jsonData["results"] as ? [[String: AnyObject]] {
    for meteoResult in meteo {

    let sunrise = meteoResult["sunrise"] as? String
    let sunset = meteoResult["sunset"] as? String
    ....

    }
    }





    (non testé)


  • macphimacphi Membre
    juin 2016 modifié #16

    Merci je vais tester ça en attendant la flatMap...



     


    EDIT :


    Non pas mieux, en fait le résultat des variables est toujours nil...


     


    J'obtiens les données avec une autre API (OpenWeatherMap) mais j'aimerais bien comprendre pourquoi ça ne le fait pas ici avec ces données JSON...


    :'(


  • J'parle pas Swift, mais: String: AnyObject ressemble à  un array de dictionary, or d'après le JSON, il n'y a pas d'array.


    Donc je ferais plus du :



    let result = jsonData["results"] as ? [String: AnyObject]
    let sunrise = result["sunrise"] as? String
  • Oui bien vu...!!!


     


    Merci beaucoup !


     



Connectez-vous ou Inscrivez-vous pour répondre.