Skip to content

Using fifo_map with base class and derived class #2023

Description

@theresa95

Hey @nlohmann and @Daniel599 !

I am currently trying to generate JSON with ordered keys and therefore used the workaround method from @Daniel599 . I just copy-pasted your workaround code and it worked as aspected. However, if I try to use it within my base and derived classes, I get an error which I do not really understand. It seems like it fails to call the to_Json methods (because the error appears if I try to map a DerivedClass-instance (test and test2) to my_json.

I have already tried the example without ordered keys (just by using json = nlohmann::json;) and it works completely fine. The keys in the output are sorted alphabetically and looks like this:

{
  "cbor": "cbortest",
  "diagnostic": "diagnose: corona",
  "header": {
    "headerId": 3,
    "timestamp": "2019-12-10T16:04:00.00Z",
    "version": "4.0.0"
  },
  "hex": "00f2",
  "roundtrip": true
}

What I am trying to achieve through using the nlohmann fifo_map is to keep the insertion order and the final output therefore should look like this:

{
  "header": {
    "headerId": 3,
    "timestamp": "2019-12-10T16:04:00.00Z",
    "version": "4.0.0"
  },
  "cbor": "cbortest",
  "hex": "00f2",
  "roundtrip": true,
  "diagnostic": "diagnose: corona"
}

Executing the following code outputs two errors:

Error C2440: 'initializing': cannot convert from 'BaseNamespace::SubNamespace::DerivedClass' to 'nlohmann::basic_json<my_workaround_fifo_map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>'	; in file: main.cpp

image

Please have a look at the following code:

In BaseClass.h:

#ifndef BASECLASS_H
#define BASECLASS_H

#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include "fifo_map.hpp"

namespace BaseNamespace{

	namespace SubNamespace{

		class BaseClass {
		public:
			BaseClass () {};
			virtual ~BaseClass () {};

			uint32_t getHeaderId() const { return headerId; };
			std::string getTimestamp() const { return timestamp; };
			std::string getVersion() const { return version; };

			void setHeaderId(uint32_t str) { headerId = str; };
			void setTimestamp(std::string str) { timestamp = str; };
			void setVersion(std::string bo) { version = bo; };

			void setHeader(UAgvHeader const& header) {
				setHeaderId(header.getHeaderId());
				setTimestamp(header.getTimestamp());
				setVersion(header.getVersion());
			}

		private:
			uint32_t    headerId;       
			std::string timestamp;     
			std::string version;        
		};

		// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
		using namespace nlohmann;
		template<class K, class V, class dummy_compare, class A>
		using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
		using my_json = basic_json<my_workaround_fifo_map>;

		void to_json(my_json &j, const BaseClass &p)
		{			
			j = my_json{
				{ "headerId", p.getHeaderId() }, 
				{ "timestamp", p.getTimestamp() }, 
				{ "version", p.getVersion() }
			};
		}

		void from_json(const my_json &j, BaseClass &p)
		{
			p.setHeaderId(j.at("headerId").get< std::uint32_t>());
			p.setTimestamp(j.at("timestamp").get< std::string >());
			p.setVersion(j.at("version").get<std::string>());
		}

	} // namespace SubNamespace

} // namespace BaseNamespace

#endif // BASECLASS_H_

In DerivedClass.h:

#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H

#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include <optional>
#include "BaseClass.h"

namespace BaseNamespace{

	namespace SubNamespace{

		class DerivedClass : public BaseClass {
		public:

			std::string getCBor() const { return cbor; };
			std::string getHex() const { return hex; };
			bool getRoundtrip() const { return roundtrip; };
			std::optional<std::string> getDiagnostic() const { return diagnostic; };

			void setCBor(std::string str) { cbor = str; };
			void setHex(std::string str) { hex = str; };
			void setRoundtrip(bool bo) { roundtrip = bo; };
			void setDiagnostic(std::optional<std::string> opt_str) { diagnostic = opt_str; };

		private:
			std::string cbor;
			std::string hex;
			bool        roundtrip;
			std::optional<std::string> diagnostic = std::nullopt;
		};
		
		// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
		using namespace nlohmann;
		template<class K, class V, class dummy_compare, class A>
		using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
		using my_json = basic_json<my_workaround_fifo_map>;

		void to_json(my_json &j, const DerivedClass& p)
		{
			j["header"] = static_cast<BaseClass>(p);
			j["cbor"] = p.getCBor();
			j["hex"] = p.getHex();
			j["roundtrip"] = p.getRoundtrip();

			// assuming you only want a "diagnostic" key if there is an actual value;
			// if not, store a nullptr and adjust the from_json accordingly
			if (p.getDiagnostic() != std::nullopt)
			{
				j["diagnostic"] = p.getDiagnostic().value();
			}
		}

		void from_json(const my_json &j, DerivedClass&p)
		{
			p.setHeader(j.at("header").get<BaseClass>());
			p.setCBor(j.at("cbor").get< std::string >());
			p.setHex(j.at("hex").get< std::string >());
			p.setRoundtrip(j.at("roundtrip").get< bool >());

			// if we also allow "null" values, then we need to add an "is_string()"
			// check
			if (j.count("diagnostic") != 0)
			{
				p.setDiagnostic(j.at("diagnostic").get< std::string >());
			}
		}

	} // namespace SubNamespace

} // namespace BaseNamespace

#endif // DERIVEDCLASS_H

In main.cpp:

#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <iomanip> 
#include <optional>
#include "DerivedClass.h"

using namespace nlohmann;
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;

int main(int argc, char* argv[]) {

	BaseNamespace::SubNamespace::DerivedClass test;
	test.setHeaderId(3);
	test.setTimestamp("2019-12-10T16:04:00.00Z");
	test.setVersion("4.0.0");
	test.setCBor("cbortest");
	test.setHex("00f2");
	test.setRoundtrip(true);
	test.setDiagnostic("diagnose: corona");
	my_json j = test;                                  // ERROR: no suitable conversion
	std::cout << std::setw(2) << j << std::endl;

	std::string str = R"({"header":
        {   "headerId" : 4711,
            "timestamp" : "1 Uhr",
		"version" : "5.0.0" 
        },
        "cbor" : "+X4A",
        "hex" : "f97e00",
        "roundtrip" : true,
        "diagnostic" : "NaN"
        })";

	my_json j2 = my_json::parse(str);
	BaseNamespace::SubNamespace::DerivedClass test2 = j2;
	my_json k = test2;                                 // ERROR: no suitable conversion

	std::cout << std::setw(2) << k << std::endl;


	return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind: questionsolution: duplicatethe issue is a duplicate; refer to the linked issue insteadsolution: proposed fixa fix for the issue has been proposed and waits for confirmation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions