Analyse fichier json

Bonjour a tous,

Je bute sur un problème, je récupère des données au format JSON de cette forme:

{"lon":10.99,"lat":44.34},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base": ...

avec le code suivant dans la variable "payload":

 Serial.print("[HTTPS] begin...\n");
    if (https.begin(*client, "https://api.openweathermap.org/..............................")) {  // HTTPS
    https.setAuthorization(meteomaticsLogin, meteomaticsPwd);
      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();

      // httpCode will be negative on error
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = https.getString();
          Serial.println(payload);

Seulement pour l'analyser, il faut que j'y intègre des \ pour échappé les ", mais le contenu de ma variable n'est pas typé String et donc "replace" ne fonctionne pas.

Comment à votre avis je pourrais résoudre le problème?

Pourtant on voit ça

Tu n'as pas besoin d'échapper les " dans une String que tu reçois pour l'analyser.
Il faut échapper les " si tu places une chaîne dans le code à compiler pour indiquer au compilateur que le " doit faire partie de la chaîne.

Pourquoi ne pas utiliser la librairie ArduinoJson que tu peux trouver dans le gestionnaire de librairie pour analyser ton json?

Merci fdufnews pour ta réponse,

Si je mets un replace sur la variable payload :

Serial.println(payload.replace('"', '\"'));

J'ai cette erreur:

G:\System\Arduino\ESP8266\scraping_web\scraping_web3\scraping_web3.ino: In function 'void setup()':
scraping_web3:72:31: error: invalid use of void expression
   72 | Serial.println(payload.replace('"', '\"'));
      |                ~~~~~~~~~~~~~~~^~~~~~~~~~~
scraping_web3:118:27: error: 'class JSONVar' has no member named 'replace'; did you mean 'replaceJson'?
  118 |   Serial.println(myObject.replace('"', '\"'));
      |                           ^~~~~~~
      |                           replaceJson
exit status 1
invalid use of void expression

J'ai essayé également essayé avec replaceJson, mais j'ai également des erreurs.

Quand à la librairie ArduinoJson, j'ai utiliser ce code, mais je n'arrive pas a accéder aux données qui sont au second niveau
Voici mon code:

JSONVar myObject = JSON.parse(payload);
            // JSON.typeof(jsonVar) can be used to get the type of the var
            if (JSON.typeof(myObject) == "undefined") {
              Serial.println("Parsing input failed!");
              return;
            }
            Serial.print("JSON object = ");
            Serial.println(myObject);
            // myObject.keys() can be used to get an array of all the keys in the object
            JSONVar keys = myObject.keys();
JSONVar myObject = JSON.parse(payload);
            // JSON.typeof(jsonVar) can be used to get the type of the var
            if (JSON.typeof(myObject) == "undefined") {
              Serial.println("Parsing input failed!");
              return;
            }
            Serial.print("JSON object = ");
            Serial.println(myObject);
            // myObject.keys() can be used to get an array of all the keys in the object
            JSONVar keys = myObject.keys();
            int count = 0;
            for (int i = 0; i < keys.length(); i++) {
              JSONVar value = myObject[keys[i]];
              sensorReadingsArr[i] = double(value);
              Serial.println(count++);
              Serial.println(sensorReadingsArr[i]);
            }

Et voici ce que je récupère dans ma boucle:

0
nan
1
nan
2
nan
3
nan
4
10000.00
5
nan
6
nan
7
1665221120.00
8
nan
9
7200.00
10
3163858.00
11
nan
12
200.00

Le mode d'emploi de replace dans la classe String est ici. replace ne renvoie rien, elle fait juste son boulot. Donc il faut faire en deux fois :

payload.replace('"', '\"');
Serial.println(payload);

Pour utiliser la bibliothèque ArduinoJson, il faut se servir de l'assistant. Tu sélectionne les options, tu colles le Json que tu veux parser et il te génère le code. Tu n'as plus qu'à sélectionner ce que tu veux garder (premier et deuxième niveaux).

Bonjour,

Je ne suis pas assez doué pour aider à dépanner votre sketch :confused:. Par contre j'ai écrit vite fait un petit bout de programme hier qui récupère aussi des données au format json sur OpenWeatherMap. J'éspère que ça pourra vous aider...


/*
* Nom du projet : OpenWeatherMap
* Auteur        : jelopo	
* Version       : 0.0.5
* Date          : 2022-10-06
* Carte         : Wemos D1 Mini
* Description   : Récupération valeurs de l'API OpenWeatherMap en JSON.
* Lien          : https://forum.arduino.cc/t/web-scraping-avec-un-esp8266/1038576/2
*
************************************************************************************
* FONCTIONS:
* Interroge le site OpenWeatherMap avec son API au format json
* Affiche une fois au démarrage dans le terminal serie les valeurs récupérées 
************************************************************************************
* INSTRUCTIONS:
* /!\ Selectionner le bon type de carte
*
* ATTENTION en cas d'erreur 401 lors de l'appel à l'API, ré-activer l'API pour
* le compte propriétaire (openweathermap.org) + attendre jusqu'à 2 heures.
* 
* Créer un fichier ~/Arduino/libraries/WifiParams/WifiParams.h et renseigner SSID 
* et PASSWD
*
* #ifndef WifiParams_h
* #define WifiParams_h
* #define ssid "SSID"
* #define password "PASSWD"
* #endif
*   
************************************************************************************
* SOURCES: 
* API OpenWeatherMAP : https://openweathermap.org/current#geo
************************************************************************************
* VERSIONS: 
* 0.0.1 - 2022-10-06 - Fichier initial.
* 0.0.2 - 2022-10-06 - Ajout Json 
* 0.0.3 - 2022-10-06 - Ajout ville, vent
* 0.0.4 - 2022-10-06 - Ajout fonction display_data
* 0.0.5 - 2022-10-06 - Ajout windDir
************************************************************************************
* TODO: 
* AAAA-MM-JJ - Modification.
*
************************************************************************************
* Nom du sketch, version, date à mettre à jour à chaque version
*/
const char sketchVersion[]= "Sketch: OpenWeatherMap, version: 0.0.5, date: 2022-10-06";

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>  // Navigateur web
#include <ArduinoJson.h>        // Installer la version 6     
#include <WifiParams.h>         // Fichier 

WiFiClient espClient;
StaticJsonDocument<1024> doc;

String appid = "renseigner ici votre APPID";
String lon="renseigner ici la longitude";
String lat="renseigner ici la latitude";

const String request = "https://api.openweathermap.org/data/2.5/weather?lat="+lat+"&lon="+lon+"&units=metric&lang=fr&appid="+appid ;
 
void setup() {
  // Initialize serial 
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.println(sketchVersion);
  setup_wifi();
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  http.useHTTP10(true);
  http.begin(client,request); // on prépare la requête
  //Serial.println(request);
  int httpCode = http.GET(); // on lance la requête
  if (httpCode = 200) { // le serveur a répondu 200 (connexion réussie)
     String reponse = http.getString();
     // Serial.println(reponse);
     deserializeJson(doc, reponse); 
     display_data() ;
  } else {
  Serial.print("Error - HTTP code : ");
  Serial.println(httpCode);
  }
}

void setup_wifi() {
  delay(10);
  // Connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.print(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  // Serial.println("WiFi connected");
}

void display_data () {
  
     String city=doc["name"];
     Serial.print("Meteo à ");
     Serial.println(city);

     float temp=doc["main"]["temp"];
     Serial.print("Température (°C)       : ");
     Serial.println(temp,1);

     int humidity=doc["main"]["humidity"];
     Serial.print("Humidité (%)           : ");
     Serial.println(humidity);
     
     int pressure=doc["main"]["pressure"];
     Serial.print("Pression (hPa)         : ");
     Serial.println(pressure);

     float windSpeed=doc["wind"]["speed"];
     windSpeed=windSpeed*3.6 ;
     Serial.print("Vitesse du vent (km/h) : ");
     Serial.println(windSpeed,0);

     int windDir=doc["wind"]["deg"];
     Serial.print("Direction du vent (°)  : ");
     Serial.print(windDir);
     Serial.print (" - ");
     String wind_rose[]={"N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSO","SO","OSO","O","ONO","NO","NNO","N"};
     int i=windDir/22.5 ; 
     Serial.println(wind_rose[i]);
         
     String sky=doc["weather"][0]["description"];
     Serial.print("Ciel                   : ");
     Serial.println(sky);

}

void loop() {

}

A+

Merci a vous deux,

Pour ton programme jelopo je vais regarder après, mais il peut m’intéresser.

Sinon pour le replace, j'ai encore quelque soucis, pour détecter le caractère a remplacer c'est ok, par contre pour le remplacer j'ai encore quelques soucis.

Voici le code:

payload.replace('\"', '\\'+'\"');

Mais avec ce code il me le remplace par ce caractère ~, ce que je ne comprend pas, quelqu'un aurait une explication?

As-tu testé ça ?
Si ça ne marche pas :
payload.replace('"', "\"");

Le premier code ne change rien dans la chaine de caractères et le deuxième ne fonctionne pas (erreur compilation).

payload.replace( '\"' , ? );

Là le premier est bon, mais impossible trouver celui de remplacement, c'est affolant je vais passer la journée sur ce problème qui parait hyper simple.
J'ai même été a penser aux regex!!

Essaye :

String S1 = String('"');
String S2 = "\"";
Serial.print(S1); Serial.print(" --> "); Serial.println("S2);
payload.replace(S1, S2);

Si ça n'affiche pas les bonnes String, essaye :

char S21 = 92; // ASCII du \ 
char S22 = 34; // ASCII du "
String S2 = String(S21)+String(S22);

Bonjour,

Tu additionnes deux caractères.

'\\' -> code ASCII 0x5C
'\"' -> code ASCII 0x22
'\\'+'\"' -> 0x5C+0x22 -> 0x7E c'est le code ASCII de tilde ~

Merci pour tes précision kamill, maintenant je comprend mieux le pourquoi du comment.

Sinon lesept merci pour ton code, j'ai finit par faire fonctionner en partie ton code, je ne sais pas pourquoi, mais le String(S22) faisait bugger la compilation, mais pas le S21.
Pour moi c'est complétement incompréhensible, mais il doit bien y avoir une raison.

Donc ce code fonctionne:

            char S21 = 92; // ASCII du \ 
            String S2 = String(S21) + '"';
            Serial.print(S1);  Serial.print(" --> ");  Serial.println(S2);
            str.replace(S1, S2);
            Serial.println(str);

J'avais de mon coté réussi à le faire fonctionner mais avec une machine à gaz avec deux boucles for et deux tableaux, ton code est bien plus sympa.

Cool ! :ok_hand:

Désolé mais je ne comprends toujours pas l'intérêt d'échapper les " dans une String pour l'analyser.
C'est une nécessité à la compilation pour insérer des " dans une chaine mais par la suite cela ne fait qu'embrouiller un peu plus le texte à traiter.

Bonjour Fdufnews,

Oui tout a fait, mais j'étais partie sur cette exemple:

Qui luit ne fonctionne qu'avec les caractères d’échappement.
Je n'ai pas dû choisir le bon exemple dès le départ. Mais ce problème de remplacement de caractères a été éducatif pour moi.

Ensuite j'avais un autre soucis, dû a mon ignorance, pour afficher mes résultats de recherche dans le JSON, le problème est que j'avais fait de la concaténation dans un "println" alors que l'on ne peut pas en Arduino.
Donc même si mon programme était bon ma sortie dans le moniteur ne l’était pas.

Petite question sur l'analyse de comment fonctionne le programme.

Je récupère mon JSON en une chaine de caractères par le biais de
https.getString()
Ensuite je le désérialise ma chaine pour la retransformer en JSON et l’analyser avec la librairie ArduinoJson.

C'est bien comme ça que cela fonctionne?

Bonjour,

De quel programme parles-tu ?
Que veux tu faire exactement des données ?
A priori tu sais récupérer les données et les mettre au format JSON.
Peux tu poster ton programme actuel, les données récupérées, et celles que tu souhaites traiter ou le format de la sortie que tu souhaites avoir ?

A+

Oui, cet exemple utilise une chaine qui est stockée en mémoire donc il faut échapper les caractères spéciaux sinon on a des erreurs de compilations.
Lorsqu'on lit une chaine d'un flux http il n'y a pas cette problématique.

Oui, le plus simple étant

  • d'ouvrir le lien que tu veux traiter dans un onglet de ton navigateur internet.
  • de copier le contenu de la page en question
  • de coller ledit contenu dans l'assitant de ArduinoJson
    Assistant | ArduinoJson 6
  • tu auras alors un code d'exemple pour accéder à tes données et aussi les arguments pour définir la taille des structures de données.

Oui c'est ce que je me suis rendu compte après.

Bonjour @jelopo,

Sympa ce petit programme. Je l'ai essayé sur un esp32, il fonctionne parfaitement. j'ai juste ajouté un http.end(); et modifié :

par if (httpCode == 200)

Merci pour le partage.
Très bonne journée à toi.