Search code examples
c++templatesnlohmann-json

C++ Issue in from_json() and to_json for templated class in nlohman json


I am trying to write and read json data of class object. Here is the code. I am using serialization/de-serialization using to_json() and from_json(). For non templated classes, things work fine, but facing issues for templated classes and structures. Below is the entire code example I am trying to build.

#include <vector>
#include <optional>
#include <iostream>
#include <memory>
#include <Eigen/Dense>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

template <class InputType = float, class OutputType = bool>
class Test {
public:
    struct InputOutputPair {
        InputType input;
        OutputType output;
    };

    void SetLeftThreshold(InputType threshold) {
        m_leftThreshold = threshold;
    }

    void SetRightThreshold(InputType threshold) {
        m_rightThreshold = threshold;
    }

    void SetLeftOutput(InputType output) {
        m_leftOutput = output;
    }

    void SetRightOutput(InputType output) {
        m_rightOutput = output;
    }

    void SetThresholdsMidpoint(InputType midThreshold) {
        m_thresholdsMidpoint = midThreshold;
    }

    void SetCurrentOutput(std::optional<OutputType> currentOutput) {
        m_currentOutput.emplace(currentOutput.value());
    }
private:
    InputType m_leftThreshold;
    InputType m_rightThreshold;
    OutputType m_leftOutput;
    OutputType m_rightOutput;
    InputType m_thresholdsMidpoint;
    std::optional<OutputType> m_currentOutput;
};

class Data {
public:
    Test<> m_leftSwitch;
};

inline void from_json(const json& j, Data& hsd) {
    j.at("isLeftSwitch").get_to(hsd.m_leftSwitch);
}

namespace nlohmann {
template <class InputType, class OutputType>
struct adl_serializer<Test<InputType, OutputType>> {
    inline void from_json(const json& j, Test<InputType, OutputType>& hsd) {
        hsd.SetLeftThreshold(j.at("leftThreshold"));
        hsd.SetRightThreshold(j.at("rightThreshold"));
        hsd.SetLeftOutput(j.at("leftOutput"));
        hsd.SetRightOutput(j.at("rightOutput"));
        hsd.SetThresholdsMidpoint(j.at("thresholdMidPoint"));
        hsd.SetCurrentOutput(j.at("currentOutput"));
    }
};
} // namespace nlohmann

int main() {

}

Demo example https://godbolt.org/z/j5Yv9xGd7

Issue - I get build errors as shown in the example

<source>:In function 'void nlohmann::to_json(json_v3_11_1::json&, const Data&)':
<source>:82:44: error: no match for 'operator=' (operand types are 'nlohmann::json_v3_11_1::json' {aka 'nlohmann::json_v3_11_1::basic_json<>'} and '<brace-enclosed initializer list>')
   82 |     j = {{"isLeftSwitch", hsd.m_leftSwitch}};

<source>: In function 'void nlohmann::from_json(const json_v3_11_1::json&, Data&)':
<source>:78:32: error: no matching function for call to 'nlohmann::json_v3_11_1::basic_json<>::get_to(Test<>&) const'
   78 |     j.at("isLeftSwitch").get_to(hsd.m_leftSwitch);

Question - Any pointers where I am going wrong in case of reading and writing templated classes using from and to json. Thank you.


Solution

  • According to the readme the adl_serializer functions need to be static.

    It is implemented like this (simplified):

    template <typename T>
    struct adl_serializer {
        static void to_json(json& j, const T& value) {
            // calls the "to_json" method in T's namespace
        }
    
        static void from_json(const json& j, T& value) {
            // same thing, but with the "from_json" method
        }
    };
    

    Once I made that change to your demo example (and commented out the from_json code since you haven't implemented any Get* functions yet) the code compiled.