const char* conversion

This feels like the wrong way to do this, but it works.

I know that root[name] is a const char*, but the class I created (not pictured here) uses fixed length char arrays (e.g. char spellName[20]). I'm not sure the best way to feed my class

#include <ArduinoJson.h>

void setup() {
  Serial.begin(9600);
  Serial.println("Hello");
  const size_t capacity = JSON_ARRAY_SIZE(6) + 6 * JSON_OBJECT_SIZE(9) + 2150;
  DynamicJsonDocument doc(capacity);

  deserializeJson(doc, F("[{\"name\":\"Mending\",\"casting_time\":\"1 minute\",\"components\":\"V,S,M\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"touch\",\"ritual\":false,\"school\":\"conjuration\",\"description\":\"This spell repairs a single break or tear in an object. As long as the break or tear is no larger than 1 foot in any dimension. This spell can physically repair a magic item or construct, but can’t restore magic.\"},{\"name\":\"Sacred Flame\",\"casting_time\":\"1 action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"evocation\",\"description\":\"The target must succeed on a Dexterity saving throw or take 1d8 radiant damage. The target gains no benefit from cover for this saving throw. At higher level 5th level (2d8), 11th level (3d8), and 17th level (4d8).\"},{\"name\":\"Spare the Dying\",\"casting_time\":\"Bonus Action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"Necromancy\",\"description\":\"You touch a living creature that has 0 hit points. The creature becomes stable. This spell has no effect on undead or constructs.\"},{\"name\":\"Thaumaturgy\",\"casting_time\":\"1 Action\",\"components\":\"V\",\"duration\":\"<1min\",\"level\":0,\"range\":\"30 feet\",\"ritual\":false,\"school\":\"Transmutation\",\"description\":\"Create one of effects within range:Voice booms 3x|Change Flames flicker, brighten, dim, or change color|Harmless tremors|Sound from a point|Door or window to fly open or slam shut|Alter the appearance of your eyes.\"},{\"name\":\"Toll the Dead\",\"casting_time\":\"1 Action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"Necromancy\",\"description\":\"One creature you can see must succeed on a Wisdom saving throw or take 1d8 necrotic damage, or 1d12 necrotic damage if HP missing. At 5th level (2d8 or 2d12), 11th level (3d8 or 3d12), and 17th level (4d8 or 4d12).\"}]"));

  for (int i = 0; i < TOME_LENGTH; i++) {
    JsonObject root = doc[i];

    char spellName[20];
    strcpy(spellName, root["name"]);
	
    Serial.println(spellName);
  }


}

void loop() {}

awoodbridge:
I'm not sure the best way to feed my class

What do you mean by "feed my class"? Does your class need to be fed in the constructor or in a method? Either way, the usual way to do this is to pass the array pointer and the array len.

Here is the full code:

#include <CircularBuffer.h>
#include <ArduinoJson.h>
#include "SpellBook.h"

const uint8_t TOME_LENGTH = 6;
CircularBuffer<SpellBook*, TOME_LENGTH> tome;


void setup() {
  Serial.begin(9600);
  Serial.println("Hello");
  const size_t capacity = JSON_ARRAY_SIZE(6) + 6 * JSON_OBJECT_SIZE(9) + 2150;
  DynamicJsonDocument doc(capacity);



  deserializeJson(doc, F("[{\"name\":\"Mending\",\"casting_time\":\"1 minute\",\"components\":\"V,S,M\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"touch\",\"ritual\":false,\"school\":\"conjuration\",\"description\":\"This spell repairs a single break or tear in an object. As long as the break or tear is no larger than 1 foot in any dimension. This spell can physically repair a magic item or construct, but can’t restore magic.\"},{\"name\":\"Sacred Flame\",\"casting_time\":\"1 action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"evocation\",\"description\":\"The target must succeed on a Dexterity saving throw or take 1d8 radiant damage. The target gains no benefit from cover for this saving throw. At higher level 5th level (2d8), 11th level (3d8), and 17th level (4d8).\"},{\"name\":\"Spare the Dying\",\"casting_time\":\"Bonus Action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"Necromancy\",\"description\":\"You touch a living creature that has 0 hit points. The creature becomes stable. This spell has no effect on undead or constructs.\"},{\"name\":\"Thaumaturgy\",\"casting_time\":\"1 Action\",\"components\":\"V\",\"duration\":\"<1min\",\"level\":0,\"range\":\"30 feet\",\"ritual\":false,\"school\":\"Transmutation\",\"description\":\"Create one of effects within range:Voice booms 3x|Change Flames flicker, brighten, dim, or change color|Harmless tremors|Sound from a point|Door or window to fly open or slam shut|Alter the appearance of your eyes.\"},{\"name\":\"Toll the Dead\",\"casting_time\":\"1 Action\",\"components\":\"V,S\",\"duration\":\"Instantaneous\",\"level\":0,\"range\":\"60 feet\",\"ritual\":false,\"school\":\"Necromancy\",\"description\":\"One creature you can see must succeed on a Wisdom saving throw or take 1d8 necrotic damage, or 1d12 necrotic damage if HP missing. At 5th level (2d8 or 2d12), 11th level (3d8 or 3d12), and 17th level (4d8 or 4d12).\"}]"));

  for (int i = 0; i < TOME_LENGTH; i++) {
    JsonObject root = doc[i];

    //char spellName[20];
    //strcpy(spellName, root["name"]);
    //Serial.println(spellName);
    
	SpellBook* newSpell = new SpellBook(root["name]", root["casting_time"], root["components"], root["duration"], root["level"], root["range"], root["ritual"], root["school"], root["description"]);
    tome.push(newSpell);
  }


}

void loop() {}

And my class.h:

#include <Arduino.h>

class SpellBook{
  public:
    char spellName[20];
    char casting_time[10];
    char components[10];
    char duration[10];
    uint8_t level;
    char range[10];
    bool ritual;
    char school[10];
    char description[300];

    SpellBook(char n[20],char ct[10],char com[10],char dur[10], uint8_t lv,char r[10],bool ri, char sc[10], char de[300]);
};

and class.cpp:

#include "SpellBook.h"

SpellBook::SpellBook(char n[20],char ct[10],char com[10],char dur[10], uint8_t lv,char r[10],bool ri, char sc[10], char de[300]) {
    strcpy(spellName, n);
    strcpy(casting_time, ct);
    strcpy(components, com);
    strcpy(duration, dur);
    level = lv;
    strcpy(range, r);
    ritual = ri;
    strcpy(school, sc);
    strcpy(description, de);
}

Instead of using fixed length arguments, just use cstring pointers:

SpellBook::SpellBook(const char *n, const char *ct, ...) {
    strncpy(spellName, n, sizeof(spellName));
    strncpy(casting_time, ct, sizeof(casting_time));
    ...
}

Since you treat the constructor's parameters as c-strings (null-terminated char arrays), that's what you should pass.

I don't understand why you've specified lengths:

SpellBook::SpellBook(char n[20],char ct[10],char com[10],char dur[10], uint8_t lv,char r[10],bool ri, char sc[10], char de[300]) {

In fact, I'd make them all 'const char *'.

Since I've never worked with ArduinoJson library, I don't know what something like:

root["casting_time"]

evaluates to . Do you?

Since I've never worked with ArduinoJson library, I don't know what something like:

root["casting_time"]

evaluates to . Do you?

It returns a const char*

Seems like you're all set then.

Danois90:
Instead of using fixed length arguments, just use cstring pointers:

SpellBook::SpellBook(const char *n, const char *ct, ...) {

strncpy(spellName, n, sizeof(spellName));
   strncpy(casting_time, ct, sizeof(casting_time));
   ...
}

I think something got lost in the translation, when I tried to implement this. I'm still getting an error.

Here is the updated SpellBook.h

#include <Arduino.h>

class SpellBook{
  public:
    const char* spellName;
    const char* casting_time;
    const char* components;
    const char* duration;
    uint8_t level;
    const char* range;
    bool ritual;
    const char* school;
    const char* description;

    SpellBook(const char* n,const char* ct,const char* com,const char* dur, uint8_t lv,const char* r,bool ri, const char* sc, const char* de);
};

SpellBook.cpp

#include "SpellBook.h"

SpellBook::SpellBook(const char* n,const char* ct,const char* com,const char* dur, uint8_t lv,const char* r,bool ri, const char* sc, const char* de) {
    strncpy(spellName, n, sizeof(spellName));
    strncpy(casting_time, ct, sizeof(casting_time));
    strncpy(components, com, sizeof(components));
    strncpy(duration, dur, sizeof(duration));
    level = lv;
    strncpy(range, r, sizeof(range));
    ritual = ri;
    strncpy(school, sc, sizeof(school));
    strncpy(description, de, sizeof(description));
}

and Error Message

sketch\SpellBook.cpp: In constructor 'SpellBook::SpellBook(const char*, const char*, const char*, const char*, uint8_t, const char*, bool, const char*, const char*)':

sketch\SpellBook.cpp:4:33: warning: argument to 'sizeof' in 'char* strncpy(char*, const char*, size_t)' call is the same expression as the destination; did you mean to provide an explicit length? [-Wsizeof-pointer-memaccess]

     strncpy(spellName, n, sizeof(spellName));

                                 ^

SpellBook.cpp:4:44: error: invalid conversion from 'const char*' to 'char*' [-fpermissive]

     strncpy(spellName, n, sizeof(spellName));

                                            ^

I'd do:

  strncpy(spellName, n, sizeof(spellName) - 1);
  spellName[sizeof(spellName) - 1] = '\0';

EDIT:
See my Reply #10 below. So much wrong with this code.

The problem is that when a function receives a pointer as a parameter, the function has no context to the length of the object that the pointer "points to". In this case, sizeof(ptr); will simply return the byte length of ptr itself, not what it points to.

When passing a pointer to a function, you should also send the length of the object as another parameter. This is why, for example, you'll see functions like this in C++:

/* using memcpy to copy structure: */
memcpy ( &person_copy, &person, sizeof(person) );

Power_Broker:
The problem is that when a function receives a pointer as a parameter, the function has no context to the length of the object that the pointer "points to". In this case, sizeof(ptr); will simply return the byte length of ptr itself, not what it points to.

Your post made me go back and look. OP is not doing what you suggest. He / She is doing something much worse -- writing to an uninitialized pointer.

The local variable in the class need to be arrays with actual storage allocated to them. The code now is an absolute disaster waiting to happen.

Finally, even if they were properly allocated arrays, why would they be 'const' if you want to copy into them?

This is for a tabletop spellbookb for Dungeons and Dragons. My dungeon master doesn’t allow phones at the table. I would like to build a little box with an an encoder and screen to scroll through my spells.

I have to be able to update spells and swap characters around. I would like to ultimately pop on a SD card that has a JSON or XML file on it. Have that parsed to show spell in a very organized way on the screen.

I would like my class to be the gate keeper of how long the text strings should be (which is why I had fixed length char arrays not what is parsed in. Is there a better way to get to this goal?

A greatly abbreviated example as I found recreating your entire "Spell Book" too tedious:

#include <Arduino.h>

class SpellBook {
  public:
    char spellName[20];
    char casting_time[10];

    SpellBook(const char* n, const char* ct);
};

SpellBook::SpellBook(const char* n, const char* ct) {
  strncpy(spellName, n, sizeof(spellName) - 1);
  spellName[sizeof(spellName) - 1] = '\0';
  strncpy(casting_time, ct, sizeof(casting_time) - 1);
  casting_time[sizeof(casting_time) - 1] = '\0';
}

void setup() {
  Serial.begin(115200);     // WHY WOULD YOU USE 9600??? THIS IS NOT 1988!!!!
  Serial.println("Hello");
  SpellBook *newSpell;

  newSpell = new SpellBook("name1", "casting_time1");
}

void loop() {}

Great, thank you. I believe that worked.