Using nlohmann fifo_map with base class and derived class

395 views Asked by At

I am currently trying to generate JSON with ordered keys and therefore used a workaround method. 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;
}
0

There are 0 answers