用nlohmann-json序列化/反序列化std::optional



我想用nlohmann-json序列化一个std::optional<float>

文档中给出的boost::optional示例似乎非常接近我想要的,但我不明白我对它的适应哪里出错了。

反序列化组件似乎在为我工作,但不是to_json方面。

下面是一个最小的工作示例

// 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 });
}
}

to_json方面将无法在MSVC中编译,出现以下错误

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

我的具体要求是

  • 如何修复?
  • 为什么这会出错,当它看起来如此接近他们的例子(boost::optional vs std::optional?)

我注意到关于将std::optionals纳入lib本身的麻烦的项目有一些相当长的讨论,尽管我不完全遵循它并理解它对我来说在实践中想要使用std::optional作为第三方对象意味着什么。的一些评论建议的方法类似于我在这里工作,所以我希望它相当于一个监督/愚蠢的错误。

干杯!

这是一个很难发现的东西,因为你太担心它是深奥的东西。这可能是相当具体到我的错误,不会太可能使用任何人在未来发现这一点。尽管如此,答案如下:

我的MWE是对真实代码库中的某些东西的简化。代码库专门使用orderd_json,但简称为json,即

using json = nlohmann::ordered_json;

nlohmann中使用json这个词的类是一个不同的、更一般的类。看起来,当序列化到ordered_json时,它需要专门针对ordered_json类型,并且不能自动导入。

。我应该让to_json接受类型ordered_json作为它的第一个参数。

static void to_json(ordered_json& j, const std::optional<T>& opt) {/*...*/}

而不是

static void to_json(json& j, const std::optional<T>& opt) {/*...*/}

事实证明from_json无论如何都能工作。