Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP8266 Object.printTo(SPIFFS_File) -> caused empty File #514

Closed
Pfannex opened this issue May 26, 2017 · 9 comments
Closed

ESP8266 Object.printTo(SPIFFS_File) -> caused empty File #514

Pfannex opened this issue May 26, 2017 · 9 comments
Labels
question v5 ArduinoJson 5

Comments

@Pfannex
Copy link

Pfannex commented May 26, 2017

Hi,

I want to write back an jsonObject to my ESP8266 FFS in my own Class.
But after writing the file by using Object.printTo(SPIFFS_File) the File is Empty.

On wich Point I make my mistake?

FFS.h

#pragma once
//FileSystem
  #include <FS.h>                //this needs to be first, or it all crashes and burns...
  #include <ArduinoJson.h>
  #include <Arduino.h>
 
//===============================================================================
//  jsonFile
//===============================================================================
class FFSjsonFile{
public:  
  FFSjsonFile(String filePath, int type);
  String filePath;
  size_t size;                               //filled in readJsonString()
  int type;                                  //0=Object; 1=Array
  String root;

  void loadFile();
  String readItem(String itemName);
  
private: 
  String readJsonString();
};
 

//===============================================================================
//  FFS 
//===============================================================================
class FFS{
public:
  FFS();
  FFSjsonFile myFile;
  void mount();  
private:
};

FFScpp

#include "FFS.h"

//===============================================================================
//  FFS 
//===============================================================================
FFS::FFS():
    //jsonFiles
    myFile("/MyFile.json", 1){
}

//  FFS public
//===============================================================================

//===> mount FFS <-------------------------------------------------
void FFS::mount(){

  Serial.print("Mounting FS...");
  if (!SPIFFS.begin()) {
    Serial.println("Failed to mount file system");
  }else{
    Serial.println("OK");
    //Serial.print("formating FS...");
    //SPIFFS.format();
    //Serial.println("OK");
    
    myFile.loadFile();
    
  }


//--- TEST----------------------------------------  
  Serial.println(myFile.readItem("Field_01"));
  Serial.println(myFile.readItem("Field_02"));


  DynamicJsonBuffer JsonBuffer;
  JsonObject& rootObject = JsonBuffer.parseObject(myFile.root);  
  rootObject["Field_01"] = "Hello";
  rootObject["Field_02"] = "World!";

  File jsonFile = SPIFFS.open("/MyFile.json", "w");
  if (!jsonFile) {
    Serial.println("Failed to open config file for writing");
    //return false;
  }

  rootObject.printTo(Serial);        // this works!
  Serial.println("");

  // writes empty file?
  rootObject.printTo(jsonFile);  
  
  jsonFile.close();
}

//===============================================================================
//  FFSjsonFile
//===============================================================================

FFSjsonFile::FFSjsonFile(String filePath, int type):
    filePath       (filePath),
    type           (type){
}

//  FFSjsonFile public
//===============================================================================

//===> loadFile <-----------------------------------------------------
void FFSjsonFile::loadFile(){
  root = readJsonString();
}

//===> readItem from jsonString <-------------------------------------
String FFSjsonFile::readItem(String itemName){
  DynamicJsonBuffer JsonBuffer;
  JsonObject& rootObject = JsonBuffer.parseObject(root);  
  return rootObject[itemName].asString();
}


//===> Read json String from File <------------------------------------
String FFSjsonFile::readJsonString(){  
    
  File jsonFile;
  String jsonData = "NIL";
 
  //if (SPIFFS.begin()) {
    Serial.print("reading " + filePath + "...");
    if (SPIFFS.exists(filePath)) {
      jsonFile = SPIFFS.open(filePath, "r");
      if (jsonFile) {
        Serial.println("OK");
        size = jsonFile.size();
        std::unique_ptr<char[]> buf(new char[size]);

        jsonFile.readBytes(buf.get(), size);
        if (type == 0){
        //Array 
          DynamicJsonBuffer jsonBufferArray;
          JsonArray& jsonArray = jsonBufferArray.parseArray(buf.get());             
          if (jsonArray.success()) {
            char buffer[size];
            jsonArray.printTo(buffer, sizeof(buffer));
            jsonData = String(buffer); 
          }
        }else{
        //Object
          DynamicJsonBuffer jsonBuffer;
          JsonObject& json = jsonBuffer.parseObject(buf.get());
          if (json.success()) {         
            char buffer[size];
            json.printTo(buffer, sizeof(buffer));
            jsonData = String(buffer);
          }
        }
      }
    }
  //}
  jsonFile.close();
  return jsonData;  
}

main.ino

#include "FFS.h"
FFS myffs;

void setup() {  
  Serial.begin(115200); 
  Serial.println("");
  myffs.mount();  
}  

void loop() {
}
@Pfannex Pfannex changed the title ESP8266 Object.printTo(FFS_File) -> empty File ESP8266 Object.printTo(FFS_File) -> caused empty File May 26, 2017
@Pfannex Pfannex changed the title ESP8266 Object.printTo(FFS_File) -> caused empty File ESP8266 Object.printTo(SPIFFS_File) -> caused empty File May 26, 2017
@bblanchon
Copy link
Owner

Sorry, I have absolutely no clue.

@Pfannex
Copy link
Author

Pfannex commented May 26, 2017

Should it work like this?
Could you verrify this problem while running the Sketch?

@Pfannex
Copy link
Author

Pfannex commented May 27, 2017

I found my mistake......

        //Object
          DynamicJsonBuffer jsonBuffer;
          JsonObject& json = jsonBuffer.parseObject(buf.get());
          if (json.success()) {         
            char buffer[size+1];    //!!!!!!!
            json.printTo(buffer, sizeof(buffer));
            jsonData = String(buffer);

I read the the json object into a String, but the buffer was one char to smal, so the "}" was lost. That was the reason while no objet was written.

My fault, sorry....

@bblanchon
Copy link
Owner

You can print directly to a String:

String jsonData;
json.printTo(jsonData);

@Pfannex
Copy link
Author

Pfannex commented May 28, 2017

String FFSjsonFile::readJsonString(){  
    
  File jsonFile;
  String jsonData = "NIL";
 
  Serial.print("reading " + filePath + "...");
  if (SPIFFS.exists(filePath)) {
    jsonFile = SPIFFS.open(filePath, "r");
    if (jsonFile) {
      Serial.println("OK");
      size = jsonFile.size();
      std::unique_ptr<char[]> buf(new char[size]);
      jsonFile.readBytes(buf.get(), size);
      
      if (type == TYPE_ARRAY){
      //Array 
        DynamicJsonBuffer jsonBufferArray;
        JsonArray& jsonArray = jsonBufferArray.parseArray(buf.get());  
        //jsonArray.printTo(jsonData);           
        if (jsonArray.success()) {
          char buffer[size];
          jsonArray.printTo(buffer, sizeof(buffer));
          jsonData = String(buffer); 
        }
      }else{
      //Object
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        if (json.success()) {         
          //char buffer[size+1];
          //json.printTo(buffer, sizeof(buffer));
          //jsonData = String(buffer);
          json.printTo(jsonData);           
          json.printTo(Serial); 
          Serial.println("");  
          Serial.println(jsonData);        
        }
      }
    }
  }
  jsonFile.close();
  return jsonData;  
}

json.printTo(jsonData);
this results also a empty object?

@bblanchon bblanchon reopened this May 28, 2017
@bblanchon
Copy link
Owner

ArduinoJson can read directly from a File:

JsonArray& jsonArray = jsonBufferArray.parseArray(jsonFile);  

Also, you can use parse() instead of parseArray() and parseObject():

JsonVariant json = jsonBuffer.parse(jsonFile);

if (json.is<JsonArray>()) {
   JsonArray& arr = json.as<JsonArray>();
   // ...
}

if (json.is<JsonObject>()) {
   JsonObject& obj = json.as<JsonObject>();
   // ...
}

@Pfannex
Copy link
Author

Pfannex commented May 29, 2017

Great hints!
I made another misstake.

json.printTo(jsonData);

seems to be additive, my string is initializized with "NIL".

String jsonData = "NIL";

that results a string like

"NIL{ "webUser":"ESPuser",......."

Now it runs very well!

String FFSjsonFile::readJsonString(){  
    
  File jsonFile;
  String jsonData;
 
  Serial.print("reading " + filePath + "...");
  if (SPIFFS.exists(filePath)) {
    jsonFile = SPIFFS.open(filePath, "r");
    if (jsonFile) {
      Serial.println("OK");
      size = jsonFile.size();
      
      DynamicJsonBuffer jsonBuffer;
      JsonVariant json = jsonBuffer.parse(jsonFile);
      if (json.is<JsonArray>()) {
        JsonArray& arr = json.as<JsonArray>();
        json.printTo(jsonData);           
      }
      if (json.is<JsonObject>()) {
        JsonObject& obj = json.as<JsonObject>();
          json.printTo(jsonData);           
      }
    }else{
      Serial.println("ERROR openFile");
      return "NIL";
    }
    jsonFile.close();
  }
  return jsonData;  
}

The reason why I want to do this, is to read the file (access to SPIFFS) only once.
Every reuse of the file-content should be happen by using the "root-String".
Also I want to pick the Object-Items by Name to read and write them.
This features should be part of an FFS-class including differnt json-file-objects.

Do you think, that my approach is the right way to do that or am I thinking to complicated again?
Maybe this feature is implemented allready??!!

Greatings
Pf@nne

@bblanchon
Copy link
Owner

bblanchon commented May 30, 2017

Hi Pf@nne,

It seems you're trying to implement the oh-so-common global configuration object.

In my opinion, it should be implemented like that (not tested):

class Config {
  DynamicJsonBuffer _jb;
  JsonObject& _root;
public:
  Config(const char* configFile) : _jb(), _root(parse(_jb, configFile)) {

  }
  JsonVariant operator[] (const char* key) const {
     return _root[key];
  }
private:
   JsonObject& parse(JsonBuffer&, const char* configFile);
};

This works OK if the configuration is read-only.
However, you need to change the configuration, the JsonBuffer will leak.


I understand this class seems a good idea, since it allows to store the application configuration in a JSON file with a convenient interface.

However, I don't like this approach for the following reasons:

  1. you need to store the whole JSON object tree -> waste of memory
  2. you need to scan object keys each time you read a value -> waste of cpu
  3. you tie all your classes with the config class -> increased coupling

In my opinion, configuration should be stored in custom structs, and they should to be converted to/from JSON with dedicated functions.
As a matter of fact, this is exactly what is described in What's the best way to use the library?

I hope this helps.

Regards,
Benoit

@Pfannex
Copy link
Author

Pfannex commented May 30, 2017

Hi Benoit,

your hints are always helpful!
Thanks for that....

to 1.)
I need access to the whole json-tree.
So I have to decide between high SPIFFS-access and big memory variables.
In my opinion it is better to take care of the Flash.
Of course only if enough memory is available.

to 2.)
OK, a struct can save CPU-resources while direct access is available, but takes the same memory space like the json-root-string.

to 3.)
It gives me a easier access to the File-Items.

Naturaly I will consider your suggestions!

Greetings
Pf@nne

Repository owner locked and limited conversation to collaborators Sep 21, 2018
@bblanchon bblanchon added the v5 ArduinoJson 5 label Feb 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question v5 ArduinoJson 5
Projects
None yet
Development

No branches or pull requests

2 participants