I wish to serialise a std::optional<float> with nlohmann-json.
The example given here in the docs for a boost::optional seems very close to what I want, but I don't understand where my adaptation of it is going wrong.
It seems that the deserialization component is working for me, but not the to_json aspect.
Here is a minimal working example
// OptionalSeriealisationTest.cpp
//#define JSON_USE_IMPLICIT_CONVERSIONS 0
// Tried toggling this as per this comment
// https://github.com/nlohmann/json/issues/1749#issuecomment-772996219 with no effect
// (no effect noticed)
#include <iostream>
#include <optional>
#include <nlohmann/json.hpp>
namespace nlohmann
{
template <typename T>
struct adl_serializer<std::optional<T>>
{
// This one is the issue
static void to_json(json& j, const std::optional<T>& opt) {
//if (opt == std::nullopt) {
// j = nullptr;
//}
//else {
// j = *opt; // this will call adl_serializer<t>::to_json which will
// // find the free function to_json in t's namespace!
//}
if (opt)
{
j = opt.value();
}
else
{
j = nullptr;
}
//NB same errors on the block above and the commented-out version
}
static void from_json(const json& j, std::optional<T>& opt) {
if (j.is_null()) {
opt = std::nullopt;
}
else {
opt = j.get<T>(); // same as above, but with
// adl_serializer<t>::from_json
}
}
};
}
using json = nlohmann::ordered_json;
int main()
{
{
// Seems ok, breakpoints on the from_json are hit
json j;
j["x"] = 4.0f;
std::optional<float> x;
j.at("x").get_to<std::optional<float>>(x);
std::cout << x.has_value() << std::endl;
std::cout << x.value() << std::endl;
}
{
// Seems ok, breakpoints on the from_json are hit
json j;
j["x"] = nullptr;
std::optional<float> x = 4.0f;
j.at("x").get_to<std::optional<float>>(x);
std::cout << x.has_value() << std::endl;
//std::cout << x.value() << std::endl;
}
{
// Won't compile on MSVC
std::optional<float> x = 4.0;
json j;
j["x"] = x;
}
{
// Won't compile on MSVC
std::optional<float> x = 4.0;
auto j = json({ "x", x });
}
}
The the to_json aspects will not compile in MSVC with the following error
Severity Code Description Project File Line Suppression State
Error (active) E0289
no instance of constructor
"nlohmann::basic_json<ObjectType, ArrayType, StringType,
BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType, JSONSerializer, BinaryType>::basic_json
[with ObjectType=nlohmann::ordered_map, ArrayType=std::vector,
StringType=std::string, BooleanType=bool, NumberIntegerType=int64_t,
NumberUnsignedType=uint64_t, NumberFloatType=double,
AllocatorType=std::allocator, JSONSerializer=nlohmann::adl_serializer,
BinaryType=std::vector<uint8_t, std::allocator<uint8_t>>]"
matches the argument list
My specific asks are
- What's the fix?
- Why does this go wrong, when it seems so close to their example (boost::optional vs std::optional?)
I note there are some quite lengthy discussions on the project about trouble incorporating std::optionals into the lib itself although I don't fully follow it and understand what it means for me in practice wanting to use std::optional as a third-party object. Some of the comments suggest approaches similar to what I have here work, so I hope it amounts to an oversight / silly mistake.
Cheers!