AliJSONRPC

RocouRocou Membre
01:27 modifié dans Xcode et Developer Tools #1
Bonjour,

Je m'adresse plus particulièrement à  Ali car je tente d'utiliser son "FrameWork" AliJSONRPC  :)

Tout semble se dérouler correctement mais j'obtiens l'erreur suivante:
"Unrecognised leading character"

Je débute totalement, je ne connaissais même pas l'existence de JSON il y a encore quelques jours.
Mon script php sur mon serveur renvoie les données suivantes:

{"id":"1","nom":"Alexis","autorisation":"t"},{"id":"2","nom":"Hubert","autorisation":"t"}

Ces données sont issues d'une requète SQL et formaté en JSON par la fonction sql2json()

Je ne vois pas trop ce qui peut clocher...

Réponses

  • wiskywisky Membre
    01:27 modifié #2
    Je pencherait pour un problème d'encodage des caractères. En général il faut utiliser l'UTF-8 pour les réponses JSON.

    Vérifie donc l'encodage du fichier .php, l'encodage utilisé pour la connexion à  la base de donnée, l'encodage utilisé dans la base de donnée pour ainsi unifier le tout et au besoin convertir les chaines dans le bon encodage ;)
  • RocouRocou Membre
    01:27 modifié #3
    dans 1300785886:

    Je pencherait pour un problème d'encodage des caractères. En général il faut utiliser l'UTF-8 pour les réponses JSON.

    Vérifie donc l'encodage du fichier .php, l'encodage utilisé pour la connexion à  la base de donnée, l'encodage utilisé dans la base de donnée pour ainsi unifier le tout et au besoin convertir les chaines dans le bon encodage ;)

    Ha oui, je n'avais pas pensé à  cela, je regarde de ce côté, merci.
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #4
    L'erreur "Unrecognized leading character" est une erreur du framework SBJSON (le framework que j'utilise qui décode les chaà®nes JSON) ; l'erreur signifie qu'il n'a pas réussi à  décoder le JSON retourné par le serveur, parce qu'il a rencontré un caractère inconnu/inattendu dans la réponse reçue sensée être du JSON.

    Et c'est vrai que ce que tu retournes avec ton code PHP n'est pas un objet JSON valide (ce qui est bizarre vu que tu l'as généré avec sql2json mais bon) : tu peux le vérifier avec des sites comme http://www.jsonlint.com/

    En effet, ton texte JSON commence par un objets puisqu'il débute avec une accolade ouvrante. Ton objet se termine un peu plus loin avec l'accolade fermante... mais est suivi d'une virgule, et d'un autre objet (autre accolade ouvrante), donc le JSON retourné contient 2 objets, alors qu'il ne devrait représenter qu'un seul objet en racine (c'est un peu si dans un arbre XML, tu avais deux noeuds racines et non un seul).

    En fait pour ton cas si tu veux retourner 2 objets il faut les encapsuler dans un tableau et retourner ce tableau de 2 objets (ce qui revient en JSON à  entourer ta réponse entre "[" et "]")
  • RocouRocou Membre
    01:27 modifié #5
    dans 1300787000:

    (ce qui est bizarre vu que tu l'as généré avec sql2json mais bon)

    En fait pour ton cas si tu veux retourner 2 objets il faut les encapsuler dans un tableau et retourner ce tableau de 2 objets (ce qui revient en JSON à  entourer ta réponse entre "[" et "]")

    Merci pour tes précisions.
    En fait, cela n'a rien de bizarre, je me demandais à  quoi servait les "[" et "]" alors j'avais modifié sql2json pour les supprimer  :D
    Bon, je vais tout reprendre mais une fois que mon serveur aura redémarré (un idiot à  voulu récupéré la table sur laquelle était posé le macMini et le câble de l'alimentation s'était déconnecté et comme je suis à  70 bornes de la machine, faire un diagnostique à  distance était un peu laborieux  B) )

    Encore merci.
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #6
    Ah ben voilà  aussi, si tu modifies ce que te génère sql2json et les fonctions standard sensées faire du JSON standard... c'est normal qu'en modifiant le JSON et enlevant des crochets il soit plus valide ^^
  • RocouRocou Membre
    01:27 modifié #7
    Bon, ça ne fonctionne pas du tout  :'(
    Crash de mon application avec comme erreur:

    -[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x4e6b9d0
    2011-03-22 15:34:53.714 Bom[88466:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x4e6b9d0'

    au débugger cela semble se passer à  l'appel de cette fonction (totalement copiée sur une des tiennes):

    -(void)afficheChauffeursWithDelegate:(id)delegate callback:(SEL)callback
    {
    service callMethodWithNameAndParams:@&quot;connectionBD&quot;,nil]<br /> setDelegate:delegate callback:callback resultClass:[Chauffeur class;
    }

    Je suppose que je ne reçois pas de données correctes en provenance du serveur mais comment afficher ce que je reçois (les données brutes) afin de comprendre ce qui se passe?
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #8
    Tu peux mettre un breakpoint dans la méthode connectionDidFinishLoading de JSONRPCResponseHandler, dans cette méthode je récupère les NSData reçues, les convertit en NSString, puis les interprète avec SBJSON.

    Ceci dit si tu regardes la callstack qu'il t'affiche quand tu crash, tu devrais avoir la pile d'appel aux fonctions et voir exactement à  quelle ligne ça crash et dans quelle méthode. Est-ce dans mon framework AliJSONRPC, et si oui dans quelle méthode et à  quelle ligne (et qu'est ce qui est reçu à  ce moment), est-ce dans ta méthode de delegate ? Est-ce que ce n'est pas ton delegate qui n'existe plus (par exemple si tu as créé un objet autorelease et qui a donc été releasé entre le moment où tu as démarré la requête et celui où tu as reçu la réponse car une requête réseau n'est pas immédiate) et du coup il essaye d'envoyer un message à  un objet qui a été détruit ? Est-ce ta resultClass Chauffeur qui n'implémente pas la méthode initWithJson (que tu dois implémenter pour permettre de convertir l'objet JSON (NSArray, NSDictionary, ...) reçu en un objet Chauffeur si tu veux l'utiliser en tant que resultClass -- bien qu'il me semblait avoir mis des sécurités/assertions si ça n'était pas le cas) ?
  • RocouRocou Membre
    01:27 modifié #9
    dans 1300808226:

    Tu peux mettre un breakpoint dans la méthode connectionDidFinishLoading de JSONRPCResponseHandler, dans cette méthode je récupère les NSData reçues, les convertit en NSString, puis les interprète avec SBJSON.

    Ceci dit si tu regardes la callstack qu'il t'affiche quand tu crash, tu devrais avoir la pile d'appel aux fonctions et voir exactement à  quelle ligne ça crash et dans quelle méthode. Est-ce dans mon framework AliJSONRPC, et si oui dans quelle méthode et à  quelle ligne (et qu'est ce qui est reçu à  ce moment), est-ce dans ta méthode de delegate ? Est-ce que ce n'est pas ton delegate qui n'existe plus (par exemple si tu as créé un objet autorelease et qui a donc été releasé entre le moment où tu as démarré la requête et celui où tu as reçu la réponse car une requête réseau n'est pas immédiate) et du coup il essaye d'envoyer un message à  un objet qui a été détruit ? Est-ce ta resultClass Chauffeur qui n'implémente pas la méthode initWithJson (que tu dois implémenter pour permettre de convertir l'objet JSON (NSArray, NSDictionary, ...) reçu en un objet Chauffeur si tu veux l'utiliser en tant que resultClass -- bien qu'il me semblait avoir mis des sécurités/assertions si ça n'était pas le cas) ?


    Alors voici la callstack que je n'arrive pas à  interpréter:

    *** Call stack at first throw:
    (
    0  CoreFoundation                      0x00ddd5a9 __exceptionPreprocess + 185
    1  libobjc.A.dylib                    0x00f31313 objc_exception_throw + 44
    2  CoreFoundation                      0x00ddf0bb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3  CoreFoundation                      0x00d4e966 ___forwarding___ + 966
    4  CoreFoundation                      0x00d4e522 _CF_forwarding_prep_0 + 50
    5  Bom                                0x00008e1e -[JSONRPCResponseHandler connectionDidFinishLoading:] + 946
    6  Foundation                          0x0006f112 -[NSURLConnection(NSURLConnectionReallyInternal) sendDidFinishLoading] + 108
    7  Foundation                          0x0006f06b _NSURLConnectionDidFinishLoading + 133
    8  CFNetwork                          0x01386492 _ZN19URLConnectionClient23_clientDidFinishLoadingEPNS_26ClientConnectionEventQueueE + 220
    9  CFNetwork                          0x014516e1 _ZN19URLConnectionClient26ClientConnectionEventQueue33processAllEventsAndConsumePayloadEP20XConnectionEventInfoI12XClientEvent18XClientEventParamsEl + 293
    10  CFNetwork                          0x014519cf _ZN19URLConnectionClient26ClientConnectionEventQueue33processAllEventsAndConsumePayloadEP20XConnectionEventInfoI12XClientEvent18XClientEventParamsEl + 1043
    11  CFNetwork                          0x0137cc84 _ZN19URLConnectionClient13processEventsEv + 100
    12  CFNetwork                          0x0137cad3 _ZN17MultiplexerSource7performEv + 251
    13  CoreFoundation                      0x00dbe8ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    14  CoreFoundation                      0x00d1c88b __CFRunLoopDoSources0 + 571
    15  CoreFoundation                      0x00d1bd86 __CFRunLoopRun + 470
    16  CoreFoundation                      0x00d1b840 CFRunLoopRunSpecific + 208
    17  CoreFoundation                      0x00d1b761 CFRunLoopRunInMode + 97
    18  GraphicsServices                    0x017341c4 GSEventRunModal + 217
    19  GraphicsServices                    0x01734289 GSEventRun + 115
    20  UIKit                              0x002dbc93 UIApplicationMain + 1160
    21  Bom                                0x0000231c main + 102
    22  Bom                                0x000022ad start + 53
    23  ???                                0x00000001 0x0 + 1
    )
    terminate called after throwing an instance of 'NSException'


    Pour le reste, j'ai scrupuleusement copié tes méthodes (comme tu peux le voir, je n'ai même pas encore modifié les commentaires  :D ).

    Ma resultClass Chauffeur est la suivante:
    @implementation Chauffeur<br />-(NSString*)identifiant { return [_jsonData objectForKey:@&quot;identifiant&quot;]?:[_jsonData objectForKey:@&quot;identifiant&quot;]; } // getCouple and getAuthor does not use the same case &quot;last&quot;/&quot;Last&quot;... pfff...<br />-(NSString*)nom { return [_jsonData objectForKey:@&quot;nom&quot;]?:[_jsonData objectForKey:@&quot;nom&quot;]; } // getCouple and getAuthor does not use the same case &quot;last&quot;/&quot;Last&quot;... pfff...<br />-(NSString*)autorisation { return [_jsonData objectForKey:@&quot;autorisation&quot;]?:[_jsonData objectForKey:@&quot;autorisation&quot;]; } // getCouple and getAuthor does not use the same case &quot;last&quot;/&quot;Last&quot;... pfff...<br />-(NSString*)description { return [NSString stringWithFormat:@&quot;&lt;Chauffeur &#092;&quot;%@ %@&#092;&quot;&gt;&quot;,self.identifiant,self.nom,self.autorisation]; }<br />@end<br />
    


    Merci pour le temps passé.
    Niveau code, je suis loin d'être à  ton niveau, beaucoup de chose sont encore obscures. Cela dit, j'ai depuis toujours, évité tout code qui demande de la réflexion à  la relecture  :D

    Sinon, je sais maintenant que le serveur retourne quelque chose de correct car tout fonctionne très bien en utilisant JSON.h
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #10
    Ok malheureusement avec cette callstack ça affiche bien ce que je présageais à  savoir que ça plante dans connectionDidFinishLaunching de JSONRPCResponseHandler, mais ça me dit pas à  quelle ligne de la méthode ça plante (d'autant que la méthode n'est pas courte)

    Cependant je pense savoir ce qui cloche (et c'est vrai que j'ai oublié de mettre une sécurité sur ce cas qui ne doit pas arriver sur tu as un vrai serveur JSONRPC en face) : ce que tu renvoies c'est du JSON, mais pas du JSON-RPC !
    Un vrai WebService JSONRPC reçoit des requêtes formattées en JSON avec une certaine structure (le nom de la méthode dans une clé donnée, les paramètres de la méthode dans une autre, etc) et retourne les résultats dans un JSON également formatté d'une certaine manière (cf http://json-rpc.org/wiki/specification)
    Or toi tu retournes directement le résultat (ton objet encodé en JSON) alors que tu devrais encapsuler ça dans un JSON contenant les clés "id", "result" et "error" qui te permettent de formatter la réponse correctement et de prévoir les cas d'erreur et tout.
  • RocouRocou Membre
    01:27 modifié #11
    dans 1300815032:

    Or toi tu retournes directement le résultat (ton objet encodé en JSON) alors que tu devrais encapsuler ça dans un JSON contenant les clés "id", "result" et "error" qui te permettent de formatter la réponse correctement et de prévoir les cas d'erreur et tout.

    Bravo, c'est ça!
    Finalement le JSON est plus complexe que je ne le pensais après avoir lu (survolé) la définition sur wikipedia (français).
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #12
    Non le JSON est simple. C'est le JSON-RPC qui n'est peut être pas aussi simple que ce que tu pensais de prime abord
    Ne pas confondre JSON et JSON-RPC.

    - JSON, c'est un format de sérialisation des objets standards, une notation textuelle des données. JSON = JavaScript Object Notation
    - RPC, qui veut dire "Remote Procedure Call", est un mécanisme permettant d'appeler (call) des méthodes (procédures) distantes (remote), par exemple d'appeler des méthodes d'un WebService depuis un client (genre un iPhone)

    Les 2 acronymes décrivent des choses distinctes, l'une est une représentation de données, l'autre un mécanisme pour communiquer entre un client et un serveur pour appeler des méthodes distantes. Tu peux utiliser le JSON tout seul complètement à  l'extérieur du contexte JSON-RPC, et les mécanismes de RPC peuvent utiliser autre chose que du JSON (par exemple du XML) pour transporter les informations indiquant la méthode à  appeler et les paramètres et le retour.

    Le JSON-RPC, c'est donc l'alliance des deux technos : utiliser le JSON comme notation/représentation/sérialisation pour envoyer et recevoir des RPC (appeler des procédures distantes et recevoir le résultat).


    Quand tu fais du JSON-RPC, tu utilises la notation JSON dans ta requête RPC pour indiquer la méthode que tu veux appeler et les paramètres à  passer à  cette méthode, et le serveur " s'il respecte bien le standard JSON-RPC " te retourne la réponse sous la forme d'un objet JSON également, qui consiste en un objet ayant une clé "id" reprenant l'identifiant de la requête (pour que tu puisses retrouver à  quelle requête correspond la réponse), une clé "result" contenant le résultat (la réponse) de la requête, et une éventuelle clé "error" contenant l'erreur en cas d'erreur lors de l'appel (méthode inexistante, paramètres manquants ou incorrects, ...)

    C'est tout ça que gère mon framework : la construction de la requête pour appeler la méthode que tu demandes et lui passer les paramètres, l'envoi de la requête, la réception de la réponse, le décodage/extraction de ladite réponse ou du code d'erreur le cas échéant, et la distribution de cette réponse ou erreur au delegate.

    ---

    Il existe d'autres standards pour communiquer à  un WebService que le JSON-RPC. Tu as le XML-RPC qui est le même principe mais utilisant du XML plutôt que du JSON pour passer le nom de la méthode, les paramètres, et récupérer le résultat ou l'erreur. Tu as le REST, où les paramètres sont passés dans l'URL directement genre "/webservice/getChauffeur/id/5", tu as le SOAP qui passe les paramètres un peu comme RPC formattés dans le corps de la requête envoyée en POST, etc, etc.

    L'avantage du JSON-RPC est qu'il est relativement léger (JSON est plus léger que XML) et simple à  mettre en oeuvre. La seule chose que tu as à  prévoir côté serveur c'est d'encapsuler ta réponse JSON dans un objet JSON ayant les clés "id","result" et éventuellement "error" si erreur il y a.
    Une autre solution assez sympa est le JSON-REST où tu passes les paramètres directement dans l'URL et la réponse t'es directement retournée en JSON. Mais du coup il faut faire du URL-Rewriting côté serveur (ou utiliser des frameworks comme Zend pour gérer le REST) et côté gestion d'erreurs il faut penser à  traiter les cas d'erreur (ce que JSON-RPC te force un peu à  faire en encapsulant la réponse dans un objet JSON [tt]{"id":"...","result":objetRetournéParTaMethodeEnJson,"error":"objet décrivant l'erreur si erreur il y a eu"}[/tt]
  • AliGatorAliGator Membre, Modérateur
    01:27 modifié #13
    Je viens de commiter sur mon github une mise à  jour du framework :
    - Je vérifie maintenant que le JSON que tu reçoit respecte la spec, pour éviter que le crash que tu as eu se reproduise si ton serveur retourne un objet non conforme à  la spec (maintenant à  la place ça remonte une erreur de type JSONRPCInternalError et met un NSLog pour expliquer le pb)
    - Commit d'un ajout que j'avais fait il y a quelques temps d'un mécanisme d'auto-retry : en effet, en conditions réelles sur iPhone il arrive que le réseau soit coupé, d'après ce que j'ai observé parce que l'équipement réseau (puce EDGE/3G) de ton iPhone est passé en veille, ce qui fait que la requête n'est pas envoyée et qu'on reçoit une erreur "Connection Lost" dans ce genre de cas. Sauf que le fait d'avoir essayé d'envoyer la requête réveille l'équipement réseau (un peu tard) et donc qu'en réenvoyant la requête une 2e fois cette fois elle passe. Du coup dans ce cas précis j'ai automatisé le renvoi de la requête une 2e fois pour réessayer de passer. Si ça passe pas après réessai, là  je remonte finalement l'erreur.

    Tu peux faire un pull / update du coup sur mon github si tu veux te mettre à  jour.
  • RocouRocou Membre
    01:27 modifié #14
    Avec beaucoup de retard, merci beaucoup!
    J'ai du préparer un contrôle en entreprise (un de plus!) qui m'a éloigné un peu de mon clavier.

    Je teste à  nouveau tout ça.
Connectez-vous ou Inscrivez-vous pour répondre.