Demande d'aide avec la lecture de fichier JSON

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Remarque :

Je ne sais pas comment tu récupères tes données JSON sur Free et comment est "architecturé" ton code, mais n'oublie pas que tu peux éventuellement envisager de récupérer ces données directement en mémoire avec HTTPRequestMemory() et CatchJSON().
Ca évite de faire appel à des SaveXxxx() et LoadXxxx()...

Si tu es intéressé, je pourrais éventuellement te mettre un code...
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Bonjour,

@boddhi, merci pour ton code et ta proposition d'aide.

Effectivement, si tu as un peu de temps, je serais intéressé par une récupération directement en mémoire si c'est possible.

Pour te donner une idée du contenu des fichiers, tu peux les récupérer sur mon site
Télécharger playlist.json
Télécharger playlist.json.txt
Télécharger playlist.m3u
Pour mon application "my player iptv" qui me permet entre autre de regarder les chaines Freebox en multiposte, je télécharge rarement la playlist des chaines, car elle change rarement.
Le fichier playlist.json est téléchargé en même temps, c'est a dire peu souvent.
J'ai besoin de ce fichier pour pouvoir avoir une correspondance "Nom de chaine"/"uuid" par exemple

Code : Tout sélectionner

      "uuid-webtv-204"   : {
          "has_abo"    : false,
          "available"  : true,
          "short_name" : "Arte",
          "has_service": true,
          "logo_url"   : "/api/latest/tv/img/channels/logos68x60/uuid-webtv-204.png",
          "name"       : "Arte",
          "uuid"       : "uuid-webtv-204"
Dans ce cas, "Arte" = "uuid-webtv-204"
Pour "TF1", ce serait "uuid-webtv-612"
J'ai accès aux chaines de la TNT car j'ai connecté une clé USB DVB-T sur le serveur Freebox Pop
Ainsi, je n'ai pas besoin que le player soit allumé.
J'ai besoin de ce "uuid-webtv-204" pour obtenir le programme TV de cette chaine (Arte) via mon application "my player iptv"
Image
Pour l'instant, je télécharge la playlist.json de manière très classique

Code : Tout sélectionner

EnableExplicit
Define JSONFILE$=".\m3u\Freebox\playlist.json"
; si le fichier playlist.json n'existe pas, on le télécharge.
If FileSize(JSONFILE$) = -1
  Debug "Le fichier json n'existe pas."
  If ReceiveHTTPFile(JSONURL$, JSONFILE$)
    Debug "Téléchargement json réussi."
  Else
    Debug "Téléchargement json non réussi."
  EndIf
Else
  Debug "Le fichier json existe."
EndIf
Je n'en ai pas terminé avec mon application iptv, je travaille dessus depuis des années pour toujours l'améliorer.
A ce jour, j'utilise des NewMap/NewList/Structure pour gérer toutes les informations des différentes playlists, mais je suis entrain de travailler sur SQLite pour toutes les bases de données. Mais tout ça prend du temps car j'ai plusieurs applications en cours.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Sur la base du source que je t'ai fourni :

Code : Tout sélectionner

EnableExplicit

Structure DONNEESCHAINE
  uuid.s
  name.s
  ; ///////////////////////////////////////
  ;avalaible.a     => Mauvaise orthographe
  available.a     
  ; ////////////////////////////////////////
  logo_url.s
  has_service.a
  short_name.s
  has_abo.a
EndStructure
; ///////////////////////////////////////
Structure CHAINE
  NomCanal.s
  DonneesChaines.DONNEESCHAINE
EndStructure
; ///////////////////////////////////////

Global NewList chaines.chaine()
;Global NewMap chaines.chaine()
Define json, jsonObjectValue

; ///////////////////////////////////////////////////////////////////////////
Define.s SourceHTTPJSON="http://pbcage.free.fr/files/playlist.json",ChaineJSON
Define *TamponJSON
*TamponJSON=ReceiveHTTPMemory(SourceHTTPJSON)
If *TamponJSON
  If CatchJSON(json,*TamponJSON,MemorySize(*TamponJSON))
    ChaineJSON=ComposeJSON(json)
    ; ------- !!!! IMPORTANT !!!! ----------
    ; Remplacer les valeurs true et false pour qu'elles puissent être récupérées via la structure DONNEESCHAINE
    ChaineJSON=ReplaceString(ChaineJSON,": true,",":1,")
    ChaineJSON=ReplaceString(ChaineJSON,":true,",":1,")
    ChaineJSON=ReplaceString(ChaineJSON,": false,",":0,")
    ChaineJSON=ReplaceString(ChaineJSON,":false,",":0,")
    ; -----------------------------
    JSON = ParseJSON(#PB_Any, ChaineJSON, #PB_JSON_NoCase)
  Else
    Debug "Echec lors de l'analyse des données JSON"
  EndIf
  FreeMemory(*TamponJSON)
Else
  Debug "Echec de la réception en mémoire du fichier JSON"
EndIf
; ///////////////////////////////////////////////////////////////////////////

If JSON
  ; get the json object value
  ; On pointe vers le membre 'success' pour obtenir sa valeur
  jsonObjectValue = GetJSONMember(JSONValue(JSON),"success")
  If jsonObjectValue ; Si l'objet 'success" a été trouvé
    If GetJSONInteger(jsonObjectValue) ; Si la valeur de 'success" est égale à 1 ('true')
      Debug "Retour Free : Succès"
      ; On pointe vers le membre 'result' pour récupérer la collection de sous-membres
      jsonObjectValue = GetJSONMember(JSONValue(JSON),"result")
      If jsonObjectValue ; si l'objet 'result' a été trouvé
        If ExamineJSONMembers(jsonObjectValue) ; si il contient des membres
          While NextJSONMember(jsonObjectValue)
            AddElement(chaines())
            With chaines()
              \NomCanal=JSONMemberKey(jsonObjectValue)
              ExtractJSONStructure(JSONMemberValue(jsonObjectValue),\DonneesChaines,DONNEESCHAINE)
            EndWith
          Wend
        EndIf
      Else
        Debug "Pas de membre 'Result' !"
      EndIf
    Else
      Debug "Retour Free : Échec"
    EndIf
  Else
    Debug "Pas de membre 'Success' !"
  EndIf  
  ; clear & release the json object
  FreeJSON(JSON)
Else
  Debug "Erreur lors du parsing !"
EndIf
ForEach chaines()
  Debug chaines()\NomCanal+" :"
  With chaines()\DonneesChaines
    Debug "  name : "+\name
    Debug "  has abo : "+\has_abo
    Debug "  available : "+\available
    Debug "  short name : "+\short_name
    Debug "  has service : "+\has_service
    Debug "  logo url : "+\logo_url
    Debug "  uuid : "+\uuid
  EndWith
Next
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Je n'ai pas encore testé la solution avec ReceiveHTTPMemory(SourceHTTPJSON) mais j'ai capitalisé a partir de ton code et ajouté la création d'une base de données SQLite playlist.db pour enregistrer toutes les données du JSON
Voici le code auquel j'arrive

Code : Tout sélectionner

EnableExplicit

Declare.a fileExist(f$)
Declare.a CheckDatabaseQuery(Database, Query$)
Declare.a CheckDatabaseUpdate(Database, Query$)
Declare.s replaceAllString(string$,find$,replace$,flags=0)

#DATABASE$ = ".\m3u\Freebox\playlist.db"
#PLAYLIST$ = ".\m3u\Freebox\playlist.m3u"
#JSONFILE$ = ".\m3u\Freebox\playlist.json"
#JSONTEXT$ = ".\m3u\Freebox\playlist.json.txt"

#PLAYURL$ = "http://mafreebox.freebox.fr/freeboxtv/playlist.m3u"
#JSONURL$ = "http://mafreebox.freebox.fr/api/v3/tv/channels/"

Structure DATAS_CHAINE
  uuid.s
  name.s
  available.a
  logo_url.s
  has_service.a
  short_name.s
  has_abo.a
EndStructure

Structure CHAINE
  NomCanal.s
  DonneesChaines.DATAS_CHAINE
EndStructure

Global NewList CHAINES.CHAINE()

Define json, jsonObjectValue

Define.s ChaineJSON

;Goto Suite1 ; Pour test

JSON = ReadFile(#PB_Any, #JSONTEXT$)
If JSON
  While Eof(JSON) = 0
    ChaineJSON + ReadString(JSON)
  Wend
  CloseFile(JSON)
EndIf

; ------- !!!! IMPORTANT !!!! ----------
; Remplacer les valeurs true et false pour qu'elles puissent être récupérées via la structure DATAS_CHAINE

ChaineJSON=ReplaceAllString(ChaineJSON,": true," ,":1,",#PB_String_NoCase)
ChaineJSON=ReplaceAllString(ChaineJSON,": false,",":0,",#PB_String_NoCase)

JSON = ParseJSON(#PB_Any, ChaineJSON, #PB_JSON_NoCase)

If JSON
  ; get the json object value
  ; On pointe vers le membre 'success' pour obtenir sa valeur
  jsonObjectValue = GetJSONMember(JSONValue(JSON),"success")
  If jsonObjectValue ; Si l'objet 'success" a été trouvé
    If GetJSONInteger(jsonObjectValue) ; Si la valeur de 'success" est égale à 1 ('true')
      Debug "Retour Free : Succès"
      ; On pointe vers le membre 'result' pour récupérer la collection de sous-membres
      jsonObjectValue = GetJSONMember(JSONValue(JSON),"result")
      If jsonObjectValue ; si l'objet 'result' a été trouvé
        If ExamineJSONMembers(jsonObjectValue) ; si il contient des membres
          While NextJSONMember(jsonObjectValue)
            AddElement(CHAINES())
            With CHAINES()
              \NomCanal=JSONMemberKey(jsonObjectValue)
              ExtractJSONStructure(JSONMemberValue(jsonObjectValue),\DonneesChaines,DATAS_CHAINE)
            EndWith
          Wend
        EndIf
      Else
        Debug "Pas de membre 'Result' !"
      EndIf
    Else
      Debug "Retour Free : Échec"
    EndIf
  Else
    Debug "Pas de membre 'Success' !"
  EndIf  
  ; clear & release the json object
  FreeJSON(JSON)
EndIf

Suite1:

UseSQLiteDatabase()

Global IN, OUT, DB, I, DATAS.s

If Not fileExist(#DATABASE$)
  OUT = CreateFile(#PB_Any, #DATABASE$)
  If OUT
    CloseFile(OUT)
  Else
    Debug "Can't create the database file !"
    End
  EndIf
EndIf

#QUERY_DELETE_TABLE="DROP TABLE IF EXISTS DATABASE"
#QUERY_CREATE_TABLE="CREATE TABLE DATABASE ("+
"uuid        VARCHAR,"+
"name        VARCHAR,"+
"available   VARCHAR,"+
"logo_url    VARCHAR,"+
"has_service VARCHAR,"+
"short_name  VARCHAR,"+
"has_abo     VARCHAR)"

;#QUERY_SELECT_TABLE="SELECT UUID,NAME FROM DATABASE ORDER BY ROWID WHERE NAME LIKE "
#QUERY_SELECT_TABLE="SELECT UUID,NAME FROM DATABASE WHERE NAME LIKE "

;Goto Suite2 ; Pour test

OUT = OpenDatabase(#PB_Any, #DATABASE$, #Empty$, #Empty$)
If OUT
  CheckDatabaseUpdate(OUT, #QUERY_DELETE_TABLE)
  CheckDatabaseUpdate(OUT, #QUERY_CREATE_TABLE)
  ForEach chaines()
    I+1
    ;Debug chaines()\NomCanal+" :"
    With chaines()\DonneesChaines
     ;Debug "  name        : "+\name
     ;Debug "  has_abo     : "+\has_abo
     ;Debug "  available   : "+\available
     ;Debug "  short_name  : "+\short_name
     ;Debug "  has_service : "+\has_service
     ;Debug "  logo_url    : "+\logo_url
     ;Debug "  uuid        : "+\uuid
     ;Solution qui ne fonctionne pas pour retirer les caractères interdits
     ;DATAS.s= "'"+\uuid+"','"+\name+"','"+\available+"','"+\logo_url+"','"+\has_service+"','"+\short_name+"','"+\has_abo+"'"
     ;CheckDatabaseUpdate(OUT, "INSERT INTO DATABASE (uuid, name, available, logo_url, has_service, short_name, has_abo) VALUES ("+DATAS+")")
     ;Solution qui fonctionne pour retirer les caractères interdits
      DATAS.s= \uuid+","+\name+","+\available+","+\logo_url+","+\has_service+","+\short_name+","+\has_abo
      SetDatabaseString(OUT, 0, StringField(DATAS, 1, ","))
      SetDatabaseString(OUT, 1, StringField(DATAS, 2, ","))
      SetDatabaseString(OUT, 2, StringField(DATAS, 3, ","))
      SetDatabaseString(OUT, 3, StringField(DATAS, 4, ","))
      SetDatabaseString(OUT, 4, StringField(DATAS, 5, ","))
      SetDatabaseString(OUT, 5, StringField(DATAS, 6, ","))
      SetDatabaseString(OUT, 6, StringField(DATAS, 7, ","))
      CheckDatabaseUpdate(OUT, "INSERT INTO DATABASE VALUES (?, ?, ?, ?, ?, ?, ?)")
    EndWith
  Next
  CloseDatabase(OUT)
  Debug "NombreDeChaines="+I
Else
    Debug "Can't open the database file for writing !"
EndIf   ; If OUT

Suite2:

DB = OpenDatabase(#PB_Any, #DATABASE$, #Empty$, #Empty$, #PB_Database_SQLite)
If DB
  Debug "Open database Ok"
  CheckDatabaseUpdate(DB, "PRAGMA auto_vacuum = FULL")
  CheckDatabaseUpdate(DB, "VACUUM")
  Debug #CRLF$+"Recherche des chaines TF1"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%TF1%'")
  Debug #CRLF$+"Recherche des chaines ARTE"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%ARTE%'")
  Debug #CRLF$+"Recherche des chaines BFM"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%BFM%'")
  Debug #CRLF$+"Recherche des chaines FRANCE"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%FRANCE%'")
  Debug #CRLF$+"Recherche des chaines HD"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%HD%'")
  CloseDatabase(DB)
  Debug Str(Round(FileSize(#DATABASE$)/1024,#PB_Round_Up))+" Ko"
EndIf ; If DB

End

Procedure.a fileExist(f$)
  If FileSize(f$) < 0
    ProcedureReturn #False
  Else
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure.s replaceAllString(string$,find$,replace$,flags=0)
  While CountString(string$, find$) > 0
    string$ = ReplaceString(string$, find$, replace$, flags)
  Wend
  ProcedureReturn string$
EndProcedure

Procedure.a CheckDatabaseQuery(Database, Query$)
  If DatabaseQuery(Database, Query$, #PB_Database_StaticCursor)
    Debug "DatabaseColumns="+DatabaseColumns(Database)
    While NextDatabaseRow(Database) ; Loop for each records
      Debug GetDatabaseString(Database, 0)+" = "+GetDatabaseString(Database, 1)
    Wend
    FinishDatabaseQuery(Database)
    ProcedureReturn #True
  Else
    Debug DatabaseError()
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure.a CheckDatabaseUpdate(Database, Query$)
  If DatabaseUpdate(Database, Query$)
    ProcedureReturn #True
  Else
    Debug DatabaseError()
    ProcedureReturn #False
  EndIf
EndProcedure

; Les outils SQLite
;
; https://sqlitebrowser.org
; https://www.sqliteexpert.com
;
; Les outils JSON
;
; https://www.mitec.cz/jsonv.html
; http://tomeko.net/software/JSONedit/
;
; Les références
;
; https://www.sqlite.org
; https://sql.sh
; https://www.sqlitetutorial.net
;
; Les articles des forums
;
;https://www.purebasic.fr/french/viewtopic.php?t=14511
;https://www.purebasic.fr/french/viewtopic.php?t=17424
;
;https://www.purebasic.fr/english/viewtopic.php?t=62501
;https://www.purebasic.fr/english/viewtopic.php?t=62502
;https://www.purebasic.fr/english/viewtopic.php?t=72004
;https://www.purebasic.fr/english/viewtopic.php?t=74732
Je vais regarder la solution de travailler en mémoire plutôt qu'avec des fichiers.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Je n'ai pas testé ton code, juste parcouru à la va-vite.

Néanmoins, deux choses :
Une remarque : Attention, dans ton fichier 'playlist.json' récupérable sur ta page Free dont tu m'as donné le lien plus haut, les booléens 'true' et 'false' étaient écrits comme suit ":true," et ":false," et non comme cela ": true," et ": false;" c-à-d sans espace après le ":".
C'est pourquoi, par rapport à mon premier code, j'ai ajouté les deux lignes :
ChaineJSON=ReplaceString(ChaineJSON,":true,",":1,")
ChaineJSON=ReplaceString(ChaineJSON,":false,",":0,")

Une question : Pourquoi traites-tu tes champs "has-abo", "available" et "has_service" comme des chaînes alors que ce sont censés être des booléens ?
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

J'ai testé (et adopté) la gestion en mémoire, ce qui m'évite de créer des fichiers qui ne servent qu'une fois, de plus c'est bien plus rapide.
Une question : Pourquoi traites-tu tes champs "has-abo", "available" et "has_service" comme des chaînes alors que ce sont censés être des booléens ?
Peux-tu préciser s'il y a une différence ou un problème a tout gérer en chaines.
J'avoue que je débute aussi bien en JSON qu'en SQLite, alors je suis preneur de toute astuce me permettant de m'améliorer et je dois dire que tu m'aides beaucoup a progresser.
Je vais regarder "BEGIN" et "COMMIT" de ton autre post
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

cage a écrit : ce qui m'évite de créer des fichiers qui ne servent qu'une fois, de plus c'est bien plus rapide.
C'était le pourquoi de ma proposition :wink:
cage a écrit : Peux-tu préciser s'il y a une différence ou un problème a tout gérer en chaines.
Problème ? Non. Différence ? Je ne suis pas assez calé en SQLite pour savoir s'il y a une réelle différence si ce n'est peut-être la place que prend ces types de données dans la base en elle-même. Un nombre requiert toujours moins d'espace qu'une chaîne surtout si cette dernière est encodée en Unicode...
Après SQLIte est assez permissif dans l'affectation des données. Affecter une chaîne "123" (ex: SetDatabaseString(#BDD,0,"123") dans un champ de type Integer ne posera aucun problème. La conversion de type sera faite automatiquement.

Toutefois, par principe (enfin moi je fonctionne ainsi), une valeur de type 'nombre' doit être gérée par un champ de type 'nombre'.
Dans PB, je ne doute pas que tu utilises des variables de type 'nombre', et non de type 'chaîne', pour gérer des nombres, non ? :D :wink:
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Dans PB, je ne doute pas que tu utilises des variables de type 'nombre', et non de type 'chaîne', pour gérer des nombres, non ? :D :wink:
Tout a fait.
J'avoue que j'ai essayé de modifier les champs de la base de donnée en utilisant VARCHAR, CHAR, TEXT, INTEGER,... et au final, la base de donnée faisait toujours la même taille. Je croyais bêtement que je gagnerais en taille au niveau du fichier, mais apparemment ce n'est pas le cas.
J'ai même joué avec des VARCHAR(X) avec X compris entre 1 et 255 et ça n'a rien changé.
J'ai donc abandonné mes tests en attendant de tomber sur une doc ou un exemple qui me parle.
Si j'ai bien compris, tu me conseille de faire comme suit:

Code : Tout sélectionner

#QUERY_CREATE_TABLE="CREATE TABLE DATABASE ("+
"uuid        VARCHAR,"+
"name        VARCHAR,"+
"available   INTEGER,"+
"logo_url    VARCHAR,"+
"has_service INTEGER,"+
"short_name  VARCHAR,"+
"has_abo     INTEGER)"
Je vais essayer de modifier le code dans ce sens et voir ce que ça donne.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Addendum : Autre point très important que j'ai oublié dans mon propos ci-avant, c'est que travailler avec champs de type 'nombre' te permettra ultérieurement de faire appel directement à des fonctions mathématiques SQLite natives dans tes requêtes (DatabaseQuery).
Par exemple (pas vraiment le plus parlant, je l'admets d'avance :wink: ), avec ton champ 'has_bo' défini en integer, tu pourrais lancer une requête "SELECT SUM (has_no) FROM DATABASE;" qui te fera la somme de tous les champs 'has_no'

Bon, ici, l'exemple n'est pas le plus évocateur puisqu'avec un champ de type 'chaîne', un "SELECT COUNT(*) FROM DATABASE WHERE has_no="1") produira le même résultat !

Mais l'idée est là !
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Je viens de faire le test, et la bd passe de 132 Ko a 128 Ko
Autrement, tout fonctionne pareil.
J'avance doucement mais surement.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Nos posts se sont croisés :)
cage a écrit : Si j'ai bien compris, tu me conseille de faire comme suit:
Oui, à moins que d'autres, plus qualifiés que moi, ne me contredisent.
Et même TEXT à la place de VARCHAR.
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Pareil, n'hésite pas à faire un DatabaseUpate(#BDD,"VACUUM") avant la fermeture de ta base. Ca la compactera.

C'est notamment nécessaire (vivement conseillé) lorsque tu supprimes des tables ou beaucoup d'enregistrements.
Seul inconvénient, c'est que c'est un processus qui demande un peu de temps (pas trop long non plus selon mon expérience), selon la taille de la base.
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Oui, j'avais trouvé ça sur internet lors de mes recherches et je l'avais adopté

Code : Tout sélectionner

DatabaseUpdate(DB, "PRAGMA auto_vacuum = FULL")
DatabaseUpdate(DB, "VACUUM")
Internet est plein d'informations, et c'est assez difficile de faire la part du bon et du mauvais.
Mais ça avait l'air de faire l'unanimité.
A priori, tu confirmes l’intérêt de cette commande.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Demande d'aide avec la lecture de fichier JSON

Message par cage »

Bonjour,

Avant tout, merci a falsam, Marc56 et plus particulièrement a boddhi pour leur aide dans l'avancement de mes projets Purebasic.
SQLite et JSON m'ont donnés du fil a retordre et sans eux je serais encore entrain de m'arracher les cheveux.

Le code ci dessous regroupe tous leurs conseils sur SQLite et JSON dans une ébauche de gestion des chaines Freebox (pour les abonnés)
a) récupération en ligne des informations de toutes les chaines diffusées par Free au format JSON
b) traitement de ces informations
c) enregistrement dans une base de données SQLite pour traitement ultérieur
d) extraction de cette BD de quelques informations sur l'identifiant d'une chaine Ex:uuid-webtv-612 = TF1
e) récupération du guide TV d'un chaine au format JSON et traitement pour afficher le guide
f) récupération d'informations sur la Freebox au format JSON + traitement et affichage de informations
J'espère que ce code sera utile a des Freenautes, mais aussi a toute personne voulant capitaliser avec SQLite et JSON
cage

Code : Tout sélectionner

EnableExplicit

Declare.a fileExist(f$)
Declare.a getProgrammeTV(chaine$)
Declare.a get_api_version()
Declare.a CheckDatabaseQuery(Database, Query$)
Declare.a CheckDatabaseUpdate(Database, Query$)
Declare.s replaceAllString(string$,find$,replace$,flags=0)

#DATABASE$ = ".\m3u\Freebox\playlist.db"
#PLAYLIST$ = ".\m3u\Freebox\playlist.m3u"
;#JSONFILE$ = ".\m3u\Freebox\playlist.json"     ; n'est plus nécessaire
;#JSONTEXT$ = ".\m3u\Freebox\playlist.json.txt" ; n'est plus nécessaire
;----------------------------------------------------------------------
;Pour les abonnés Free (Freebox)
#FBX_BASEURL$ = "http://mafreebox.freebox.fr"
#FBX_CONNECT$ = "http://mafreebox.freebox.fr/login.php"
#FBX_PLAYURL$ = "http://mafreebox.freebox.fr/freeboxtv/playlist.m3u"
#FBX_JSONURL$ = "http://mafreebox.freebox.fr/api/v3/tv/channels"
#FBX_VERSION$ = "http://mafreebox.freebox.fr/api_version"
#FBX_DOC_URL$ = "http://mafreebox.freebox.fr/doc/index.html"
;----------------------------------------------------------------------

Structure DATAS_CHAINE
  uuid.s
  name.s
  available.a
  logo_url.s
  has_service.a
  short_name.s
  has_abo.a
EndStructure

Structure CHAINE
  NomCanal.s
  DonneesChaine.DATAS_CHAINE
EndStructure

Structure DATAS_GUIDE
  sub_title.s
  _next.s
  id.s
  duration.c
  picture.s
  desc.s
  picture_big.s
  categorie_name.s
  title.s
  prev.s
  categorie.a
  episode_number.c
  saison_number.c
  date.c
EndStructure

Structure GUIDE
  Horodatage.s
  DonneesGuide.DATAS_GUIDE
EndStructure

Global NewList CHAINES.CHAINE()
Global NewList MYGUIDE.GUIDE()

Define JSON, jsonObjectValue

Define ChaineJSON.s

Define *TamponJSON

*TamponJSON=ReceiveHTTPMemory(#FBX_JSONURL$)
If *TamponJSON
  JSON = CatchJSON(#PB_Any, *TamponJSON, MemorySize(*TamponJSON))
  If JSON
    ChaineJSON=ComposeJSON(JSON)
    ; ------- !!!! IMPORTANT !!!! ----------
    ; Remplacer les valeurs true et false pour qu'elles puissent être récupérées via la structure DATAS_CHAINE
    ChaineJSON=ReplaceAllString(ChaineJSON,": true," ,":1,",#PB_String_NoCase)
    ChaineJSON=ReplaceAllString(ChaineJSON,":true,"  ,":1,",#PB_String_NoCase)
    ChaineJSON=ReplaceAllString(ChaineJSON,": false,",":0,",#PB_String_NoCase)
    ChaineJSON=ReplaceAllString(ChaineJSON,":false," ,":0,",#PB_String_NoCase)
    ; --------------------------------------
    JSON = ParseJSON(#PB_Any, ChaineJSON, #PB_JSON_NoCase)
  Else
    Debug "Echec lors de l'analyse des données JSON"
  EndIf
  FreeMemory(*TamponJSON)
Else
  Debug "Echec de la réception en mémoire du fichier JSON"
EndIf

If JSON
  ; get the json object value
  ; On pointe vers le membre 'success' pour obtenir sa valeur
  jsonObjectValue = GetJSONMember(JSONValue(JSON),"success")
  If jsonObjectValue ; Si l'objet 'success" a été trouvé
    If GetJSONInteger(jsonObjectValue) ; Si la valeur de 'success" est égale à 1 ('true')
      Debug "Retour Free : Succès"
      ; On pointe vers le membre 'result' pour récupérer la collection de sous-membres
      jsonObjectValue = GetJSONMember(JSONValue(JSON),"result")
      If jsonObjectValue ; si l'objet 'result' a été trouvé
        If ExamineJSONMembers(jsonObjectValue) ; si il contient des membres
          While NextJSONMember(jsonObjectValue)
            AddElement(CHAINES())
            With CHAINES()
              \NomCanal=JSONMemberKey(jsonObjectValue)
              ExtractJSONStructure(JSONMemberValue(jsonObjectValue),\DonneesChaine,DATAS_CHAINE)
            EndWith
          Wend
        EndIf
      Else
        Debug "Pas de membre 'Result' !"
      EndIf
    Else
      Debug "Retour Free : Échec"
    EndIf
  Else
    Debug "Pas de membre 'Success' !"
  EndIf  
  ; clear & release the json object
  FreeJSON(JSON)
EndIf

UseSQLiteDatabase()

Global IN, OUT, DB, I, DATAS.s

If Not fileExist(#DATABASE$)
  OUT = CreateFile(#PB_Any, #DATABASE$)
  If OUT
    CloseFile(OUT)
  Else
    Debug "Can't create the database file !"
    End
  EndIf
EndIf

#QUERY_DELETE_TABLE="DROP TABLE IF EXISTS DATABASE"
#QUERY_CREATE_TABLE="CREATE TABLE DATABASE ("+
"uuid        TEXT,"+
"name        TEXT,"+
"available   INTEGER,"+
"logo_url    TEXT,"+
"has_service INTEGER,"+
"short_name  TEXT,"+
"has_abo     INTEGER)"

;#QUERY_SELECT_TABLE="SELECT UUID,NAME FROM DATABASE ORDER BY ROWID WHERE NAME LIKE "
#QUERY_SELECT_TABLE="SELECT UUID,NAME FROM DATABASE WHERE NAME LIKE "

OUT = OpenDatabase(#PB_Any, #DATABASE$, #Empty$, #Empty$)
If OUT
  CheckDatabaseUpdate(OUT, #QUERY_DELETE_TABLE)
  CheckDatabaseUpdate(OUT, #QUERY_CREATE_TABLE)
  I=0
  CheckDatabaseUpdate(OUT, "BEGIN")
  ForEach CHAINES()
    I+1
   ;Debug CHAINES()\NomCanal+" :"
    With CHAINES()\DonneesChaine
     ;Debug "  name        : "+\name
     ;Debug "  has_abo     : "+\has_abo
     ;Debug "  available   : "+\available
     ;Debug "  short_name  : "+\short_name
     ;Debug "  has_service : "+\has_service
     ;Debug "  logo_url    : "+\logo_url
     ;Debug "  uuid        : "+\uuid
     ;Solution qui ne fonctionne pas pour retirer les caractères interdits
     ;DATAS.s= "'"+\uuid+"','"+\name+"','"+\available+"','"+\logo_url+"','"+\has_service+"','"+\short_name+"','"+\has_abo+"'"
     ;CheckDatabaseUpdate(OUT, "INSERT INTO DATABASE (uuid, name, available, logo_url, has_service, short_name, has_abo) VALUES ("+DATAS+")")
     ;Solution qui fonctionne pour retirer les caractères interdits
      DATAS.s= \uuid+","+\name+","+\available+","+\logo_url+","+\has_service+","+\short_name+","+\has_abo
      SetDatabaseString(OUT, 0, StringField(DATAS, 1, ","))
      SetDatabaseString(OUT, 1, StringField(DATAS, 2, ","))
      SetDatabaseString(OUT, 2, StringField(DATAS, 3, ","))
      SetDatabaseString(OUT, 3, StringField(DATAS, 4, ","))
      SetDatabaseString(OUT, 4, StringField(DATAS, 5, ","))
      SetDatabaseString(OUT, 5, StringField(DATAS, 6, ","))
      SetDatabaseString(OUT, 6, StringField(DATAS, 7, ","))
      CheckDatabaseUpdate(OUT, "INSERT INTO DATABASE VALUES (?, ?, ?, ?, ?, ?, ?)")
    EndWith
  Next
  ClearList(CHAINES())
  CheckDatabaseUpdate(OUT, "COMMIT")
  CheckDatabaseUpdate(OUT, "PRAGMA auto_vacuum = FULL")
  CheckDatabaseUpdate(OUT, "VACUUM")
  CloseDatabase(OUT)
  Debug "NombreDeChaines="+I
Else
    Debug "Can't open the database file for writing !"
EndIf   ; If OUT

DB = OpenDatabase(#PB_Any, #DATABASE$, #Empty$, #Empty$, #PB_Database_SQLite)
If DB
  Debug "Open database Ok"
  CheckDatabaseUpdate(DB, "PRAGMA auto_vacuum = FULL")
  CheckDatabaseUpdate(DB, "VACUUM")
  Debug #CRLF$+"Recherche des chaines TF1"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%TF1%'")
  Debug #CRLF$+"Recherche des chaines ARTE"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%ARTE%'")
  Debug #CRLF$+"Recherche des chaines BFM"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%BFM%'")
  Debug #CRLF$+"Recherche des chaines FRANCE"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%FRANCE%'")
  Debug #CRLF$+"Recherche des chaines HD"
  CheckDatabaseQuery(DB, #QUERY_SELECT_TABLE+"'%HD%'")
  CloseDatabase(DB)
  Debug Str(Round(FileSize(#DATABASE$)/1024,#PB_Round_Up))+" Ko"
EndIf ; If DB
Debug "----------------------------------------------"
get_api_version()
Debug "----------------------------------------------"
getProgrammeTV("uuid-webtv-404")
Debug "----------------------------------------------"
getProgrammeTV("uuid-webtv-612")
Debug "----------------------------------------------"

End

Procedure.a fileExist(f$)
  If FileSize(f$) < 0
    ProcedureReturn #False
  Else
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure.s replaceAllString(string$,find$,replace$,flags=0)
  While CountString(string$, find$) > 0
    string$ = ReplaceString(string$, find$, replace$, flags)
  Wend
  ProcedureReturn string$
EndProcedure

Procedure.a CheckDatabaseQuery(Database, Query$)
  If DatabaseQuery(Database, Query$, #PB_Database_StaticCursor)
   ;Debug "DatabaseColumns="+DatabaseColumns(Database)
    While NextDatabaseRow(Database) ; Loop for each records
      Debug GetDatabaseString(Database, 0)+" = "+GetDatabaseString(Database, 1)
    Wend
    FinishDatabaseQuery(Database)
    ProcedureReturn #True
  Else
    Debug DatabaseError()
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure.a CheckDatabaseUpdate(Database, Query$)
  If DatabaseUpdate(Database, Query$)
    ProcedureReturn #True
  Else
    Debug DatabaseError()
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure.a getProgrammeTV(chaine$)
;;http://mafreebox.freebox.fr/api/v3/tv/epg/by_channel/<channel_id>/<epoch_time>
  Protected epoch_time$ = Str(Date())
  Protected channel_id$ = chaine$
  Protected url$ = "http://mafreebox.freebox.fr/api/v3/tv/epg/by_channel/"
  Protected JSON, jsonObjectValue
  Protected ChaineJSON$, Heure$
  Protected *TamponJSON
 ;Debug url$+channel_id$+"/"+epoch_time$
  *TamponJSON = ReceiveHTTPMemory(url$+channel_id$+"/"+epoch_time$)
  If *TamponJSON
    JSON = CatchJSON(#PB_Any, *TamponJSON, MemorySize(*TamponJSON))
    If JSON
      ChaineJSON$ = ComposeJSON(JSON)
      ; ------- !!!! IMPORTANT !!!! ----------
      ; Remplacer les valeurs true et false pour qu'elles puissent être récupérées via la structure DATAS_GUIDE
      ChaineJSON$=ReplaceAllString(ChaineJSON$,": true," ,":1,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,":true,"  ,":1,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,": false,",":0,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,":false," ,":0,",#PB_String_NoCase)
      ; --------------------------------------
      JSON = ParseJSON(#PB_Any, ChaineJSON$, #PB_JSON_NoCase)
    Else
      Debug "Echec lors de l'analyse des données JSON"
    EndIf
  FreeMemory(*TamponJSON)
  Else
    Debug "Echec de la réception en mémoire du fichier JSON"
  EndIf
  ;;;
  If JSON
    ; get the json object value
    ; On pointe vers le membre 'success' pour obtenir sa valeur
    jsonObjectValue = GetJSONMember(JSONValue(JSON),"success")
    If jsonObjectValue ; Si l'objet 'success" a été trouvé
      If GetJSONInteger(jsonObjectValue) ; Si la valeur de 'success" est égale à 1 ('true')
       ;Debug "Retour Free : Succès"
        ; On pointe vers le membre 'result' pour récupérer la collection de sous-membres
        jsonObjectValue = GetJSONMember(JSONValue(JSON),"result")
        If jsonObjectValue ; si l'objet 'result' a été trouvé
          If ExamineJSONMembers(jsonObjectValue) ; si il contient des membres
            While NextJSONMember(jsonObjectValue)
              AddElement(MYGUIDE())
              With MYGUIDE()
                \Horodatage=JSONMemberKey(jsonObjectValue)
                ExtractJSONStructure(JSONMemberValue(jsonObjectValue),\DonneesGuide,DATAS_GUIDE)
              EndWith
            Wend
          EndIf
        Else
          Debug "Pas de membre 'Result' !"
        EndIf
      Else
        Debug "Retour Free : Échec"
      EndIf
    Else
      Debug "Pas de membre 'Success' !"
    EndIf
    ; clear & release the json object
    FreeJSON(JSON)
    ; On trie la liste MYGUIDE() en fonction de la valeur Horodatage (TimeStamp)
    SortStructuredList(MYGUIDE(), #PB_Sort_Ascending,OffsetOf(GUIDE\Horodatage), TypeOf(GUIDE\Horodatage))
    ForEach MYGUIDE()
      I+1
      Heure$ = MYGUIDE()\Horodatage ;: Debug Heure$
      Heure$ = StringField(Heure$,1,"_")
      Heure$ = FormatDate("%yyyy/%mm/%dd %hh:%ii:%ss",Val(Heure$))
      Debug #CRLF$+Heure$
      With MYGUIDE()\DonneesGuide
        If \categorie_name <> #Empty$ : Debug \categorie_name : EndIf
        If \title          <> #Empty$ : Debug \title          : EndIf
        If \sub_title      <> #Empty$ : Debug \sub_title      : EndIf
        If \saison_number             : Debug "Saison: " +\saison_number         : EndIf
        If \episode_number            : Debug "Episode: "+\episode_number        : EndIf
        If \duration                  : Debug "Durée: "  +Str(\duration/60)+"mn" : EndIf
        If \desc           <> #Empty$
          \desc=replaceAllString(\desc,". ",#CRLF$)
          Debug \desc
        EndIf
      EndWith
    Next
    ClearList(MYGUIDE())
  EndIf
EndProcedure

Procedure.a get_api_version()
;;http://mafreebox.freebox.fr/api_version
  Protected url$ = "http://mafreebox.freebox.fr/api_version"
  Protected JSON, jsonObjectValue
  Protected ChaineJSON$
  Protected *TamponJSON
  *TamponJSON = ReceiveHTTPMemory(url$)
  If *TamponJSON
    JSON = CatchJSON(#PB_Any, *TamponJSON, MemorySize(*TamponJSON))
    If JSON
      ChaineJSON$ = ComposeJSON(JSON)
      ; ------- !!!! IMPORTANT !!!! ----------
      ; Remplacer les valeurs true et false pour qu'elles puissent être récupérées via la structure DATAS_GUIDE
      ChaineJSON$=ReplaceAllString(ChaineJSON$,": true," ,":1,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,":true,"  ,":1,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,": false,",":0,",#PB_String_NoCase)
      ChaineJSON$=ReplaceAllString(ChaineJSON$,":false," ,":0,",#PB_String_NoCase)
      ; --------------------------------------
      JSON = ParseJSON(#PB_Any, ChaineJSON$, #PB_JSON_NoCase)
    Else
      Debug "Echec lors de l'analyse des données JSON"
    EndIf
  FreeMemory(*TamponJSON)
  Else
    Debug "Echec de la réception en mémoire du fichier JSON"
  EndIf
  If JSON
    ; get the json object value
    ; On pointe vers le membre 'success' pour obtenir sa valeur
    jsonObjectValue = GetJSONMember(JSONValue(JSON),"device_name")
    Debug "Nom de la Box: "+GetJSONString(jsonObjectValue)
    jsonObjectValue = GetJSONMember(JSONValue(JSON),"box_model_name")
    Debug "Modèle de la Box: "+GetJSONString(jsonObjectValue)
    jsonObjectValue = GetJSONMember(JSONValue(JSON),"box_model")
    Debug "Modèle de la Box: "+GetJSONString(jsonObjectValue)
    jsonObjectValue = GetJSONMember(JSONValue(JSON),"api_version")
    Debug "Version de la Box: "+GetJSONString(jsonObjectValue)
    ; clear & release the json object
    FreeJSON(JSON)
  Else
    Debug "Echec lors de l'analyse des données JSON"
  EndIf
  ;api_base_url    "/api/"
  ;https_port      42182
  ;device_name     "FBCAGE"
  ;https_available true
  ;box_model       "fbxgw8-r1"
  ;api_domain      "wuk0txyh.fbxos.fr"
  ;uid             "18c6e2d9b696fd3634aeec9fa1f5f8b1"
  ;api_version     "10.2"
  ;device_type     "FreeboxServer8,1"
EndProcedure

; Les outils SQLite
;
; https://sqlitebrowser.org
; https://www.sqliteexpert.com
;
; Les outils JSON
;
; https://www.mitec.cz/jsonv.html
; http://tomeko.net/software/JSONedit/
;
; Les références
;
; https://www.sqlite.org
; https://sql.sh
; https://www.sqlitetutorial.net
; https://www.timestamp.fr
; https://www.epochconverter.com
; https://fr.wikipedia.org/wiki/Horodatage
;
; Les articles des forums
;
;https://www.purebasic.fr/french/viewtopic.php?t=18716
;https://www.purebasic.fr/french/viewtopic.php?t=19045
;
;https://www.purebasic.fr/french/viewtopic.php?t=14511
;https://www.purebasic.fr/french/viewtopic.php?t=17424
;
;https://www.purebasic.fr/english/viewtopic.php?t=62501
;https://www.purebasic.fr/english/viewtopic.php?t=62502
;https://www.purebasic.fr/english/viewtopic.php?t=72004
;https://www.purebasic.fr/english/viewtopic.php?t=74732
;
; Autres ressources
;
;https://dev.freebox.fr/bugs/
;https://dev.freebox.fr/sdk/os/#
;https://dev.freebox.fr/sdk/os/upload/#ws-upload-api
;https://dev.freebox.fr/sdk/server.html
;https://dev.freebox.fr/sdk/player.html
;https://dev.freebox.fr/sdk/libfbxqml/
;https://dev.freebox.fr/sdk/telec.html
;https://github.com/fbx/
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Demande d'aide avec la lecture de fichier JSON

Message par boddhi »

Salut cage

Deux remarques :
1) Il y a un travail d'optimisation à faire : En effet, des portions de code sont redondantes
2) Le recours à une GUI détaillant le déroulement des processus (avec leurs résultats - réussite ou échec) serait un plus :wink: surtout si tu veux en faire un exécutable (lequel accélérerait également l'exécution du code)
Répondre