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

Create JsonObject subclass #2056

Closed
ewoudwijma opened this issue Feb 16, 2024 · 7 comments
Closed

Create JsonObject subclass #2056

ewoudwijma opened this issue Feb 16, 2024 · 7 comments
Labels
question v6 ArduinoJson 6

Comments

@ewoudwijma
Copy link

Description
I want to create a subclass of JsonObject and tried this:

class VarObject: public JsonObject {
const char * getID() {return this[“id”];}
// more functions doing specific things with the object elements
}

But I get compile errors eg converter related.

Probably it’s just a generic c++ question but can this be done, eg using friend, or defining other functions calling superclass functions ?

Troubleshooter's report

  1. The program uses ArduinoJson 6
  2. The issue happens at compile time
  3. The error is not in the list

Environment

  • Microcontroller: ESP32
  • Core/Framework: ESP32 expressif
  • IDE: PIO

Reproduction code

class VarObject: public JsonObject {
  const char * getID() {return this[“id”];}
  // more functions doing specific things with the object elements
}
@bblanchon bblanchon added the v6 ArduinoJson 6 label Feb 17, 2024
@bblanchon
Copy link
Owner

Hi @ewoudwijma,

JsonObject was never designed to be inherited.
You should use composition instead.

I should probably add final to these classes.

Best regards,
Benoit

@bblanchon
Copy link
Owner

I must postpone final to v8 because it breaks existing libraries.

Nevertheless, I stand with what I said: JsonObject was never designed to be inherited and you should use composition instead.
However, if you want to go against my recommendation, you can fix you code like so:

  class VarObject: public JsonObject {
-   const char * getID() {return this["id"];}
+   const char * getID() {return (*this)["id"];}
    // more functions doing specific things with the object elements
  };

@ewoudwijma
Copy link
Author

ewoudwijma commented Feb 18, 2024

I have to balance a few arguments in my head:

a) I don't want to go against your recommendation. Although except from one compile error it works:

VarObject var = vars.add<VarObject>();

results in:

VariantRefBase.hpp:74:71: error: no type named 'type' in 'struct ArduinoJson::V703PB2::detail::enable_if<false, ArduinoJson::V703PB2::JsonObject>'

I think It might be because VarObject is not considered a JsonVariant ... ???

b) I use JsonObjects all over the place and don't want to change the way they are used: the normal ArduinoJson way will change in case of composition in varObject.var["one"] = 2 etc. and I really prefer varObject["one"] = 2.

c) if it will be final in 8, I have a problem then

d) alternatively I can make 'ugly' global functions like

const char * varID(JsonObject var) {return var["id"].as<const char *>();}

So I guess I should choose d), unless you give me a hint to resolve a) in case I will doubt between a) and d) ;-)

So I don't expect you to answer, just shared my thoughts ;-)

Remark: since the creation of this issue(2 days ago) I changed from ArduinoJson v6 to v7 (1 day ago)

@bblanchon
Copy link
Owner

I think It might be because VarObject is not considered a JsonVariant ...

Indeed, the function is add<JsonObject>(), not add<VarObject>(), but you could add your own function in the derived class.

I really prefer varObject["one"] = 2.

You could override operator[] and delegate to the JsonObject, but it might be complicated.

I recommend using custom data types to store the state of the application, as shown in JsonConfigFile.ino.
You can use converters to ease the conversion between custom types and JsonDocument.
See also: Why should I create a separate config object?

@ewoudwijma
Copy link
Author

ewoudwijma commented Feb 22, 2024

You could override operator[] and delegate to the JsonObject, but it might be complicated.

Indeed too complicated as this was only one example, I need about all the functions 🙂. So I decided to go for option B, it’s not the most beautiful but the most pragmatic option.

I recommend using custom data types to store the state of the application, as shown in JsonConfigFile.ino.
You can use converters to ease the conversion between custom types and JsonDocument.
See also: Why should I create a separate config object?

Yeah, this is I think about the biggest challenge when using ArduinoJson. It is so beautiful but advice is to only use it to transfer non json data structure to json and vice versa.
I am a big fan of json and as I use it to create the ui, and use it to store it persistent on fs, and have an application which has a quite dynamic data model which changes depending on the ui interactions, for me using json is a no brainer.

This is the UI and most of the elements you see here can be added, removed or changed depending on what the user does / the values of the model in json format.

image

So maybe C(++) is not the right choice for me but I a have most experience in c++ and previous tests with python were not satisfying and hey, I am still building for esp32 so most code and libraries and performance can be found in c++.

So I use ArduinoJson full blown. The app model is present infinitely. And it works! With AJ v6 model size grew slow enough to never hit boundaries and with v7 the heap even gets cleaned up👍

I manipulate AJ data everywhere except in loop (with a few read exceptions I still need to optimize but even that runs blazingly fast). For loop c data structs are used which are filled by AJ.

Another advantage of AJ is the support for custom data types. My most used class is called Coord3D (x,y,z) and besides ordinary functions it has operator functions plus implements your converter stuff and it is extremely powerful to use it this way.

So yeah, I am a bit depending on AJ and hope it will not disappear from the world 🙂.

I am one of the more active developers of WLED, especially the WLED MoonModules fork so learned using AJ there. Currently I am finishing a new application, see above screenshot.
If you like it, it would be very nice to talk with you on this topic as it might be more efficient then typing 🙂. With our MoonModules team we have regular confcalls so you are more then welcome to join a session if you have the time (you can dm me if you want eg ewowi on discord)

Kind regards,
Ewoud

@bblanchon
Copy link
Owner

Hi Ewoud,

Like any programming advice, my recommendations are only valid in certain contexts.
There are probably situations where defining a structure to hold the configuration or state of the application is not practical.

Therefore, I can easily understand why you'd use JsonVariant or JsonDocument everywhere: it's simple, flexible, and convenient.
However, I do not see why you'd derive from JsonVariant.
I could understand why you'd want to create a facade on top of JsonVariant, but in that case, you should use composition, not inheritance.

Anyway, thank you very much for the kind words about the library. I love reading users' testimonies, but they are rare, unfortunately. ArduinoJson should not disappear anytime soon, but you can ensure the durability of the library by sponsoring the project 😉

Your project looks fantastic. I hope you can show me a demo someday.

Best regards,
Benoit

@ewoudwijma
Copy link
Author

ewoudwijma commented Feb 23, 2024

Hi Benoit,
I said I choose for option B, but I meant D: just make convenience functions. So no derivation or composition.
Thx for your response! Let's keep in touch for a demo.

Oh and I bought your book earlier this week as a way of sponsoring 🙂

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question v6 ArduinoJson 6
Projects
None yet
Development

No branches or pull requests

2 participants