Derived classes from json class
bradacsa opened this issue · comments
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
- The bug also occurs if the latest version from the
develop
branch is used. - I can successfully compile and run the unit tests.
Hi can I work on this problem?
Hi can I work on this problem?
Sure.
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:
My derived is wrapped in something else:
Maybe the object id is a filter at inheritance? Sorry if I ask something stupid...
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.
I looked into it but I have a hard time figuring it out. I think its because of some typechecking during runtime.
How did you define the constructor of Class Cookie?
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...
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;
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
I think you are right I have no clue why that is not working
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.
I think we need some help from someone that has more experience with this codebase.
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
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.
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; }
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.
There's something weird with the conversion from
Cookie
tonlohmann::json
to store in thecJsonFile
. I would recommend thatCookie
have anlohmann::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.
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);
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.