bblanchon / ArduinoJson

📟 JSON library for Arduino and embedded C++. Simple and efficient.
https://arduinojson.org
MIT License
6.63k stars 1.1k forks source link

Initial serializing #2079

Closed hitecSmartHome closed 2 months ago

hitecSmartHome commented 2 months ago

Hello I need some help!

I'm using Arduino as an IDF component. My device is an ESP32-Wrover-E ( 16mb ram and 4mb psram ) I'm using ArduinoJson V7.

I have a global char array which is allocated from External ram. This char array is used for syncronisation purposes across my code. It contains fresh information about components.

Here is my function which I have problem with.

boolean ComponentHandler::updateZones(JsonObject newZoneData){
    JsonDocument zonesObj;
    DeserializationError error = deserializeJson(zonesObj, (const char*)zones);
    if(error){ zonesObj.clear(); } // I have tried to just ignore the error

    std::string id = newZoneData["id"];
    if( zonesObj[id].isNull() ){
        JsonObject zone = zonesObj[id].to<JsonObject>();
        zone.set( newZoneData );
    }else{
        zonesObj[id].set( newZoneData );
    }
    return serializeJson(zonesObj, zones, ZONES_SIZE) > 0;
}

The thing is that initially the zones char array is empty. So the deserializeJson will fail. The zones object looks something like this:

{
 "restzthhgf":{
     "thing1":5
  },
 "dgfcvtrtrh":{
     "thing1":5
  }
}

When the zones char array is empty sometimes I get 2 from the serializeJson which is bigger than 0 so the function will return true.

I'm allocating ram for it like this

if( !hshSystem.alloc(zones,ZONES_SIZE) ){
    printf("FAILED TO ALLOCATE ZONES ARRAY\n");
}

alloc method is just a wrapper for ps_malloc

boolean Sys::alloc(char *&ptr, int size) {
    ptr = (char *)ps_malloc(size * sizeof(char));
    if (!ptr || ptr == NULL) {
        return false;
    }
    return true;
}

So the idea of this function is that it tries to deserialize the zones array first so I can have an object representation of the global zones. After that it checks if a zone with the given id exists. If it exists, it will overwrite, if it does not exists it creates it. But when serialization happens, i got 2 as the output.

bblanchon commented 2 months ago

Hi @hitecSmartHome,

It looks like you can simplify your code like so:

  boolean ComponentHandler::updateZones(JsonObject newZoneData){
      JsonDocument zonesObj;
      DeserializationError error = deserializeJson(zonesObj, (const char*)zones);
      if(error){ zonesObj.clear(); } // I have tried to just ignore the error

-     std::string id = newZoneData["id"];
-     if( zonesObj[id].isNull() ){
-         JsonObject zone = zonesObj[id].to<JsonObject>();
-         zone.set( newZoneData );
-     }else{
-         zonesObj[id].set( newZoneData );
-     }
+     zonesObj[newZoneData["id"]] = newZoneData;
      return serializeJson(zonesObj, zones, ZONES_SIZE) > 0;
  }

But what is your question exactly?

Best regards, Benoit

hitecSmartHome commented 2 months ago

Thanks for the suggestion.

The question is that if I serialize it why do i get string "2" instead of a string object.

hitecSmartHome commented 2 months ago

It doesn't work like that

boolean ComponentHandler::updateZones(JsonObject newZoneData){
    JsonDocument zonesObj;
    deserializeJson(zonesObj, (const char*)zones); // Doesn't matter if the zones char array is empty or not.
    zonesObj[newZoneData["id"]] = newZoneData; // Does not save it. I have to use set() method
    return serializeJson(zonesObj, zones, ZONES_SIZE) > 0;
}
bblanchon commented 2 months ago

Indeed, I just realized that doc[var] doesn't work when var is a string; it only works with integers. This is clearly a bug. Until I fix it, please use zonesObj[newZoneData["id"].as<JsonString>()] = newZoneData; instead.

About your original issue, where serializeJson() produces 2 as the output JSON document, this could be due to the initial data in the zones buffer. Please add a terminator at the beginning of the buffer to ensure that deserializeJson() sees an empty input.

  if( !hshSystem.alloc(zones,ZONES_SIZE) ){
      printf("FAILED TO ALLOCATE ZONES ARRAY\n");
+     return;
  }
+ zones[0] = 0;
hitecSmartHome commented 2 months ago

I see. Will check it, thank you very much! And I will try with JsonString also.

hitecSmartHome commented 2 months ago

Confirmed it is working with JsonString.

boolean ComponentHandler::updateZones(JsonObject newZoneData){
    JsonDocument zonesObj;
    deserializeJson(zonesObj, (const char*)zones);
    zonesObj[newZoneData["id"].as<JsonString>()] = newZoneData;
    return serializeJson(zonesObj, zones, ZONES_SIZE) > 0;
}

Thank you very much. Will test this

if( !hshSystem.alloc(zones,ZONES_SIZE) ){
      printf("FAILED TO ALLOCATE ZONES ARRAY\n");
+     return;
  }
+ zones[0] = 0;
hitecSmartHome commented 2 months ago

It works fine. Thank you very much for the help. I appreciate it.