HELP - Deserialize Json

Hello

I'm having some difficulties extracting a specific type of JSON and storing them in variables.

The JSON is in this format:

{"BLOCKA": {"UNIT1": {"names":["Filipe","Ana"]},"UNIT2": {"names":["Lorraine","Matheus"]}},"BLOCKB": {"UNIT3": {"names":["Filipe","Ana"]},"UNIT4": {"names":["Lorraine","Matheus"]}}}

There are fields called "BLOCKS", inside each block there are other fields called unit and inside each unit is an array of names. Therefore, I would need to extract the data accordingly. For example, I would need to access the names that are within units of a certain block, but I'm having a lot of difficulty doing this.

Through the Arduino Json website, in the assistant, it generates a code for me but the code is based on that exact amount of information, but I would like to do it with any amount of data, so I can't work the code generated by them.

Code generated by Arduino Json assistant:

// String input;

StaticJsonDocument<512> doc;

DeserializationError error = deserializeJson(doc, input);

if (error) {
  Serial.print("deserializeJson() failed: ");
  Serial.println(error.c_str());
  return;
}

for (JsonPair BLOCKA_item : doc["BLOCKA"].as<JsonObject>()) {
  const char* BLOCKA_item_key = BLOCKA_item.key().c_str(); // "UNIT1", "UNIT2"

  const char* BLOCKA_item_value_names_0 = BLOCKA_item.value()["names"][0]; // "Filipe", "Lorraine"
  const char* BLOCKA_item_value_names_1 = BLOCKA_item.value()["names"][1]; // "Ana", "Matheus"

}

for (JsonPair BLOCKB_item : doc["BLOCKB"].as<JsonObject>()) {
  const char* BLOCKB_item_key = BLOCKB_item.key().c_str(); // "UNIT3", "UNIT4"

  const char* BLOCKB_item_value_names_0 = BLOCKB_item.value()["names"][0]; // "Filipe", "Lorraine"
  const char* BLOCKB_item_value_names_1 = BLOCKB_item.value()["names"][1]; // "Ana", "Matheus"

}

My code:

#include "ArduinoJson.h"

String input =
 "{\"BLOCKA\": {\"101\": {\"names\":[\"Filipe\",\"Ana\"]},\"102\": {\"names\":[\"Lorraine\",\"Matheus\"]}},\"BLOCKB\": {\"201\": {\"names\":[\"Claudia\",\"Carla\"]},\"202\": {\"names\":[\"Rodolfo\",\"Cleber\"]}}}";

class Unit
{
  public:
    String building;
    String unit[];
    String names[][99];
};

Unit units[99];

void setup() {
  Serial.begin(9600);
  classUnit(input);
  Serial.println("TEST");
  Serial.println(units[0].building);
  Serial.println(units[1].building);
  Serial.println(units[0].unit[0]);
  Serial.println(units[0].unit[1]);
}

void loop() {


}

void classUnit (String json) {
  DynamicJsonDocument doc(30000);
  deserializeJson(doc, json);
  JsonObject root = doc.as<JsonObject>();
  int count = 0;
  int count2;
  int count3;
  for (JsonPair kv : root)
  {
    count2 = 0;
    units[count].building = kv.key().c_str();
    Serial.println(units[count].building);
    for (JsonPair BUILDING_item : doc[units[count].building].as<JsonObject>()) {
      count3 = 0;
      units[count].unit[count2] = BUILDING_item.key().c_str();
      Serial.println(units[count].unit[count2]);
      for (JsonPair names_item : doc[units[count].building][units[count].unit[count2]]["names"].as<JsonObject>()) {
        units[count].names[count2][count3] = names_item.key().c_str();
        Serial.println(units[count].names[count2][count3]);
        count3++;
      }
      count2++;
    }
    count++;
  }
}

In my code, I am not able to correctly store the variables. Could someone help me?

would that help?

#include <ArduinoJson.h>

const size_t MAX_INPUT_LENGTH = 512;

const char json[MAX_INPUT_LENGTH] = "{\"BLOCKA\":{\"UNIT1\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT2\":{\"names\":[\"Lorraine\",\"Matheus\"]}},\"BLOCKB\":{\"UNIT3\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT4\":{\"names\":[\"Lorraine\",\"Matheus\"]}}}";

StaticJsonDocument<512> doc;

void setup() {
  Serial.begin(115200);

  DeserializationError error = deserializeJson(doc, json, MAX_INPUT_LENGTH);

  if (error) {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }

  for (JsonPair kv : doc.as<JsonObject>()) {
    Serial.println(kv.key().c_str()); // Blocks
    for (JsonPair blockItem : doc[kv.key().c_str()].as<JsonObject>()) {
      const char* blockItemKey = blockItem.key().c_str(); // Units
      Serial.write('\t'); Serial.println(blockItemKey);
      for (JsonVariant v : blockItem.value()["names"].as<JsonArray>()) { // Names
        Serial.write("\t\t"); Serial.println(v.as<const char *>());
      }
    }
    Serial.println("--------------------");
  }
}

void loop() {}

the serial monitor (at 115200 bauds) should show

BLOCKA
	UNIT1
		Filipe
		Ana
	UNIT2
		Lorraine
		Matheus
--------------------
BLOCKB
	UNIT3
		Filipe
		Ana
	UNIT4
		Lorraine
		Matheus
--------------------

1 Like

Thank you very much @J-M-L

I need to store this data in an organized way. In which I know that, for example, there is an "Ana" and I need to know that it is in "UNIT3" in "BLOCKB".

So I tried to store the data like this:

#include <ArduinoJson.h>

const size_t MAX_INPUT_LENGTH = 512;

const char json[MAX_INPUT_LENGTH] = "{\"BLOCKA\":{\"UNIT1\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT2\":{\"names\":[\"Lorraine\",\"Matheus\"]}},\"BLOCKB\":{\"UNIT3\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT4\":{\"names\":[\"Lorraine\",\"Matheus\"]}}}";

StaticJsonDocument<512> doc;

class Unit {
  public:
    const char* blocks;
    const char* units[];
    const char* names[][50];
};

Unit unit[99];

void setup() {
  Serial.begin(115200);
  int count = 0;
  int count2;
  int count3;
  DeserializationError error = deserializeJson(doc, json, MAX_INPUT_LENGTH);

  if (error) {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }

  for (JsonPair kv : doc.as<JsonObject>()) {
    count2 = 0;
    unit[count].blocks = kv.key().c_str();
    Serial.println(unit[count].blocks); // Blocks
    for (JsonPair blockItem : doc[kv.key().c_str()].as<JsonObject>()) {
      count3 = 0;
      unit[count].units[count2] = blockItem.key().c_str(); // Units
      Serial.write('\t'); Serial.println(unit[count].units[count2]);
      for (JsonVariant v : blockItem.value()["names"].as<JsonArray>()) { // Names
        unit[count].names[count2][count3] = v.as<const char *>();
        Serial.write("\t\t"); Serial.println(unit[count].names[count2][count3]);
        count3++;
      }
      count2++;
    }
    Serial.println("--------------------");
    count++;
  }

  //TEST VARIABLES
  Serial.println("Blocks:");
  for (int i = 0; i < 2; i++) {
    Serial.println(unit[i].blocks);
  }
  Serial.println("Units");
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      Serial.println(unit[i].units[j]);
    }
  }
  Serial.println("Names");
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
      for (int x = 0; x < 2; x++) {
        Serial.println(unit[i].names[j][x]);
      }
    }
  }
}

void loop() {}

And my result on the serial monitor was this:

11:55:26.795 -> BLOCKA
11:55:26.795 -> UNIT1
11:55:26.795 -> Filipe
11:55:26.795 -> Ana
11:55:26.795 -> UNIT2
11:55:26.795 -> Lorraine
11:55:26.795 -> Matheus
11:55:26.795 -> --------------------
11:55:26.842 -> BLOCKB
11:55:26.842 -> UNIT3
11:55:26.842 -> Filipe
11:55:26.842 -> Ana
11:55:26.842 -> UNIT4
11:55:26.842 -> Lorraine
11:55:26.842 -> Matheus
11:55:26.842 -> --------------------
11:55:26.842 -> Blocks:
11:55:26.842 -> BLOCKA
11:55:26.842 -> BLOCKB
11:55:26.842 -> Units
11:55:26.842 -> BLOCKB
11:55:26.842 -> Filipe
11:55:26.842 -> Filipe
11:55:26.842 -> UNIT4
11:55:26.842 -> Names
11:55:26.842 -> BLOCKB
11:55:26.842 -> Filipe
11:55:26.842 -> Lorraine
11:55:26.842 -> Lorraine
11:55:26.842 -> Filipe
11:55:26.842 -> UNIT4
11:55:26.842 -> Lorraine
11:55:26.842 -> Matheus

For some reason the information is not being stored correctly and I'm not sure why...

you need a tree structure as you have tenants within units within blocks

try something like that

#include <ArduinoJson.h>

const size_t MAX_INPUT_LENGTH = 512;

const char json[MAX_INPUT_LENGTH] = "{\"BLOCKA\":{\"UNIT1\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT2\":{\"names\":[\"Lorraine\",\"Matheus\"]}},\"BLOCKB\":{\"UNIT3\":{\"names\":[\"Filipe\",\"Ana\"]},\"UNIT4\":{\"names\":[\"Lorraine\",\"Matheus\"]}}}";

StaticJsonDocument<512> doc;

const size_t maxBlocks = 5;
const size_t maxUnits = 5;
const size_t maxTenants = 5;

// -------------------------------------------------------------------------------------------------------------
// REMEMBER YOU ONLY STORE POINTERS BACK INTO THE JSON STRUCTURE
// SO THE LIFESPAN OF THE BLOCKS' CONTENT IS RELATED TO THE LIFESPAN
// OF THE JSON BUFFER YOUR DESERIALIZED
// -------------------------------------------------------------------------------------------------------------

struct t_unit {
  const char* unitName;
  size_t tenantCount;
  const char* tenantName[maxTenants];
};

struct t_block {
  const char* blockName;
  size_t unitCount;
  t_unit unit[maxUnits];
};

t_block blocks[maxBlocks];
size_t blockCount = 0;
// -------------------------------------------------------------------------------------------------------------


void setup() {
  Serial.begin(115200);
  DeserializationError error = deserializeJson(doc, json, MAX_INPUT_LENGTH);

  if (error) {
    Serial.print("deserializeJson() failed: ");
    Serial.println(error.c_str());
    return;
  }

  size_t currentBlock = 0;
  size_t currentUnit = 0;
  size_t currentTenant = 0;

  for (JsonPair kv : doc.as<JsonObject>()) { // for each block
    blocks[currentBlock].blockName = kv.key().c_str();
    currentUnit = 0;
    for (JsonPair blockItem : doc[kv.key().c_str()].as<JsonObject>()) { // for each unit in this block
      blocks[currentBlock].unit[currentUnit].unitName = blockItem.key().c_str(); // Units
      currentTenant = 0;
      for (JsonVariant v : blockItem.value()["names"].as<JsonArray>()) { // for each teanant in this unit in this block
        blocks[currentBlock].unit[currentUnit].tenantName[currentTenant] = v.as<const char *>();
        if (++currentTenant >= maxTenants) break; // no more space
      }
      blocks[currentBlock].unit[currentUnit].tenantCount = currentTenant;
      if (++currentUnit >= maxUnits) break; // no more space
    }
    blocks[currentBlock].unitCount = currentUnit;
    if (++currentBlock >= maxBlocks) break; // no more space
  }
  blockCount = currentBlock;

  // print what we got
  for (size_t b = 0; b < blockCount; b++ ) { // for each valid block
    Serial.println(blocks[b].blockName);
    for (size_t u = 0; u < blocks[b].unitCount; u++ ) { // for each valid unit in this block
      Serial.print("\t"); Serial.println(blocks[b].unit[u].unitName);
      for (size_t t = 0; t < blocks[b].unit[u].tenantCount; t++ ) { // for each valid tenant in this unit in this block
        Serial.print("\t\t"); Serial.println(blocks[b].unit[u].tenantName[t]);
      }
    }
  }
}

void loop() {}

it should print

BLOCKA
	UNIT1
		Filipe
		Ana
	UNIT2
		Lorraine
		Matheus
BLOCKB
	UNIT3
		Filipe
		Ana
	UNIT4
		Lorraine
		Matheus

please take into account the comment

// REMEMBER YOU ONLY STORE POINTERS BACK INTO THE JSON STRUCTURE
// SO THE LIFESPAN OF THE BLOCKS' CONTENT IS RELATED TO THE LIFESPAN
// OF THE JSON BUFFER YOUR DESERIALIZED

➜ as you have the JSON deserialised, you could also just use the ArduinoJSON library to access this data. You are NOT making any copy of the names. if you wanted to do that, you would need a char array of some size for each name and use strcpy() (or better strlcpy()) instead of just remembering the pointer to the name.

1 Like

Thank you very much, it helped me a lot!

Two doubts: for me to make the copy I need to use strlcpy() and for that to modify the variable types like this, right?

struct t_unit {
  char unitName[20];
  size_t tenantCount;
  const char* tenantName[maxTenants];
};

struct t_block {
  char blockName[20]; 
  size_t unitCount;
  t_unit unit[maxUnits];
};

In the case of the variable const char* tenantName[maxTenants]; how would it look?

The other question is regarding the maximum sizes. If I change everything to 50, for example, it gives a compilation error for the board (ESP32). Is there any explanation or is it due to the available memory on the board itself?

Thank you!

something like this

const size_t maxBlocks = 5;
const size_t maxUnits = 5;
const size_t maxTenants = 5;
const size_t maxName = 20; // including the trailing null char

struct t_unit {
  char unitName[maxName];
  size_t tenantCount;
  char tenantName[maxTenants][maxName];
};

struct t_block {
  char blockName[maxName];
  size_t unitCount;
  t_unit unit[maxUnits];
};

or you create a tenant structure if you want separate types all the way (might be useful if you collect other infos)

const size_t maxBlocks = 5;
const size_t maxUnits = 5;
const size_t maxTenants = 5;
const size_t maxName = 20; // including the trailing null char

struct t_tenant {
  char tenantName[maxName];
};

struct t_unit {
  char unitName[maxName];
  size_t tenantCount;
  t_tenant tenant[maxTenants];
};

struct t_block {
  char blockName[maxName];
  size_t unitCount;
  t_unit unit[maxUnits];
};

so a block can have maxUnits units, which can have maxTenants tenants (which might eat up a lot of memory if you increase those numbers)

But how would I save a copy of a JsonVariant?

strcpy(blocks[currentBlock].unit[currentUnit].tenantName[currentTenant][currentNames], v.as<char*>());

Regardless of how I try to make the copy, the error was just:

invalid conversion from 'char' to 'char*' [-fpermissive]

with which struct version?

struct t_unit {
  char unitName[maxName];
  size_t tenantCount;
  char tenantName[maxTenants][maxName];
};

Then the pointer to the array’s first char where to store the name is blocks[currentBlock].unit[currentUnit].tenantName[currentTenant] or &(blocks[currentBlock].unit[currentUnit].tenantName[currentTenant] [0])

So you can copy this way

strlcpy(blocks[currentBlock].unit[currentUnit].tenantName[currentTenant], v.as<char*>(), maxName);

Thank you so much @J-M-L ! :smiley:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.