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

Derived classes from json class #4316

Open
2 tasks done
bradacsa opened this issue Mar 18, 2024 · 19 comments
Open
2 tasks done

Derived classes from json class #4316

bradacsa opened this issue Mar 18, 2024 · 19 comments

Comments

@bradacsa
Copy link

bradacsa commented Mar 18, 2024

Description

Equal operator won't work with derived classes.

Reproduction steps

class Cookie : public nlohmann::json <- this can handle cookies, chops up cookie strings from headers, etc...
class JsonFile : public nlohmann::json <- this is nothing else but the json class joined up with a fstream to make easier to load and save json from file.

Cookie A1("A1=d=AQABBN3h6GUCECMYqmLClG1pK2uiuaCh0xoFEgABCAEq6mUTZutMb2UBAiAAAAcI3OHoZXSoivI&S=AQAAAqvfHaGFJkLfQ07HNDr2tH0;Expires=Thu, 6 Mar 2025 213629 GMT;"); <- this will create a json object

Pass it to JsonFile
FileHandlers::JsonFile cJsonFile(cookieJson);
cJsonFile["Cookies"]["A1"] = A1; <- this drops error: "terminate called after throwing an instance of 'nlohmann::json_abi_v3_11_2::detail::type_error' what(): [json.exception.type_error.302] type must be string, but is object"

nlohmann::json::parse(A1.dump()) <- this works, but it seems like a not ideal workaroud

Expected vs. actual results

I expect that as an nlohmann::json derived object it should be passed as
nlohmann::json x = { {"one", 1}, {"two", 2} };
nlohmann::json y;
y["thingy"] = x;

Minimal code example

In reproduction steps.

Error messages

"terminate called after throwing an instance of 'nlohmann::json_abi_v3_11_2::detail::type_error' what(): [json.exception.type_error.302] type must be string, but is object"

Compiler and operating system

GCC, Ubuntu

Library version

3.11.2

Validation

@arthikek
Copy link

Hi can I work on this problem?

@nlohmann
Copy link
Owner

Hi can I work on this problem?

Sure.

@bradacsa
Copy link
Author

I'm a beginner-like guy in C++ and I don't want to say something idiotic thing, but when I did some debugging, VSCode shows some differences.
The normal shows a nlohmann baseclass:
nl01
My derived is wrapped in something else:
nl02

Maybe the object id is a filter at inheritance? Sorry if I ask something stupid...

@gregmarr
Copy link
Contributor

The difference is because you derive from json. The tooltip doesn't show the type of the class itself, just the base classes. The top of the lower one is the actual type of nlohmann::json, which is an alias.

@arthikek
Copy link

I looked into it but I have a hard time figuring it out. I think its because of some typechecking during runtime.

@arthikek
Copy link

How did you define the constructor of Class Cookie?

@bradacsa
Copy link
Author

class Cookie : public nlohmann::json
{
      Cookie(const std::string& cookieName, const std::string cookieValue)
      {
          (*this)["name"] = cookieName;
          (*this)["value"] = cookieValue;
      }
      
      Cookie(const std::string& cookieString)
      {
          _chopCString(cookieString);
      }
      ~Cookie()
      {}
};

_chopCString() is private function to chop up HTML header format cookies and create json key-value pairs.
And now I immediately see that I just forgot : nlohmann::json() from the end of the constructors...

Update:
I tried with the Cookie(const std::string& cookieName, const std::string cookieValue) : nlohmann::json() version as well, but the error is the same...
image

@arthikek
Copy link

arthikek commented Mar 21, 2024

I was able to write the following test that passes. I used constructor delegation from the json parent class to construct the object properly.
This is the test I wrote:

//
// Created by arthi on 20-3-2024.
//
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
#include <string>
using nlohmann::json;

class Cookie : public json
{
  public:
    Cookie(const std::string& cookieName, const std::string cookieValue ,const std::string jsonValue ) :json(jsonValue)
    {
//        this->operator[]("name") = cookieName;
//        this->operator[]("value") = cookieValue;
    }





};

class JsonFile : public json
{
};

TEST_CASE("Derived classes handling") {
    SECTION("Assign derived object to json") {
        Cookie A1("testCookie", "A1=d=AQABBN3h6GUCECMYqmLClG1pK2uiuaCh0xoFEgABCAEq6mUTZutMb2UBAiAAAAcI3OHoZXSoivI&S=AQAAAqvfHaGFJkLfQ07HNDr2tH0;Expires=Thu, 6 Mar 2025 213629 GMT;",R"({ {"one", 1}, {"two", 2} })");
        JsonFile cJsonFile;
        cJsonFile["Cookies"]["A1"] = A1;
        CHECK(cJsonFile["Cookies"]["A1"].dump() == A1.dump());
    }
}

Still, I have problems with the following lines:

//        (*this)["name"] = cookieName;
//        (*this)["value"] = cookieValue;

@bradacsa
Copy link
Author

bradacsa commented Mar 21, 2024

Well, maybe the problem stems from my misunderstanding of object inheritance...
So, if I derive my own class from the json class and use the (*this) pointer, I essentially refer to the class itself, including the base class in which I want to create the json structure. This way I can supplement the json class with my own functions.

If I want to create a json object, I could create it like

nlohmann::json js;
js["key"] = "value";

This means that I can create key-value pairs in the inherited class as well, right?

This way it will be like for example:

Cookie cookie("thisisthecookiename", "thisisthecookievalue");
std::cout << "CookieName:" << cookie["name"] << std::endl;
std::cout << "CookieValue:" << cookie["value"] << std::endl;

This will prints out:

CookieName: thisisthecookiename
CookieValue: thisisthecookievalue

@arthikek
Copy link

I think you are right I have no clue why that is not working

@bradacsa
Copy link
Author

I was thinking that when json takes over the parameters of the derived class, it filters based on some object id, so that either std::string or a json_base class can be passed. I still don't fully understand everything about C++, but it seems like the compiler doesn't like something around std::forward.

I also tried passing it as cJsonFile["Cookies"]["A1"] = static_cast<nlohmann::json>(A1); but that didn't work either, drops the same error.

@arthikek
Copy link

I think we need some help from someone that has more experience with this codebase.

@gregmarr
Copy link
Contributor

Can you post a full code example that shows the problem? You can use this as a starting point. https://www.godbolt.org/z/93osKEKEs

@bradacsa
Copy link
Author

Here it goes, Sir:
https://www.godbolt.org/z/6Ezx973eW

I didn't post the full JsonFile class, because it has a few dependencies of my own classes (in the original you can pass trough a filepath and it tries to open it that will be the load/save file for the json object, but basically that's the only difference), but the error is the same here.
The Cookie class works, you can pass a std::cout << A1["value"] << std::endl; before cJsonFile["A1"] = A1; line, it will give back the "d=AQABBN3h6GUCECMYqmLClG1pK2uiuaCh0xoFEgABCAEq6mUTZutMb2UBAiAAAAcI3OHoZXSoivI&S=AQAAAqvfHaGFJkLfQ07HNDr2tH0" value.

@gregmarr
Copy link
Contributor

There's something weird with the conversion from Cookie to nlohmann::json to store in the cJsonFile. I would recommend that Cookie have a nlohmann::json member rather than deriving from it.

This reproduces the error:

    #define JSON_USE_IMPLICIT_CONVERSIONS 0
    #include <nlohmann/json.hpp>

    Cookie A1("A1=d=AQABBN3h6GUCECMYqmLClG1pK2uiuaCh0xoFEgABCAEq6mUTZutMb2UBAiAAAAcI3OHoZXSoivI&S=AQAAAqvfHaGFJkLfQ07HNDr2tH0;Expires=Thu, 6 Mar 2025 213629 GMT;");
    nlohmann::json cookie = A1;

This change removes the error:

class Cookie 
{
    private:
        nlohmann::json j;
...
            void _chopCString(const std::string& cookieString)
            {
...
                                j["name"] = tempKey;
                                j["name"] = tempKey;
...
                if(tempKey!="") j[tempKey] = tempValue;
            }
...
            operator nlohmann::json const &() { return j; }

@gregmarr
Copy link
Contributor

Minimal reproduction scenario:

#include <nlohmann/json.hpp>
class derived : public nlohmann::json{};
int main()
{
    nlohmann::json t = derived();
}

So there's apparently something wrong with the constructor selection logic.

@bradacsa
Copy link
Author

There's something weird with the conversion from Cookie to nlohmann::json to store in the cJsonFile. I would recommend that Cookie have a nlohmann::json member rather than deriving from it.

This reproduces the error:

    #define JSON_USE_IMPLICIT_CONVERSIONS 0
    #include <nlohmann/json.hpp>

    Cookie A1("A1=d=AQABBN3h6GUCECMYqmLClG1pK2uiuaCh0xoFEgABCAEq6mUTZutMb2UBAiAAAAcI3OHoZXSoivI&S=AQAAAqvfHaGFJkLfQ07HNDr2tH0;Expires=Thu, 6 Mar 2025 213629 GMT;");
    nlohmann::json cookie = A1;

This change removes the error:

class Cookie 
{
    private:
        nlohmann::json j;
...
            void _chopCString(const std::string& cookieString)
            {
...
                                j["name"] = tempKey;
                                j["name"] = tempKey;
...
                if(tempKey!="") j[tempKey] = tempValue;
            }
...
            operator nlohmann::json const &() { return j; }

Thanks!
I think I will stick to the nlohmann::json::parse(A1.dump()) workaround until this problem will be fixed.

@gregmarr
Copy link
Contributor

If you don't want to change like that, then you can also do this to clean up the selection.

    cJsonFile["A1"] = static_cast<nlohmann::json const &>(A1);

@bradacsa
Copy link
Author

bradacsa commented Mar 23, 2024

If you don't want to change like that, then you can also do this to clean up the selection.

    cJsonFile["A1"] = static_cast<nlohmann::json const &>(A1);

Oh, thanks a lot! This is what I'm looking for! So basically it's not a simple nlohmann::json cast, but a const reference... that's why it didn't work for me before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants