nlohmann-json序列化std::optional时to_json编译错误问题
std::optional适配nlohmann-json序列化编译错误修复
问题现象
需要基于nlohmann-json库实现std::optional<T>类型的序列化与反序列化,参考官方boost::optional适配示例编写的逻辑存在单边异常:
- 反序列化(from_json)功能可正常运行,断点可正常命中
- 序列化(to_json)相关逻辑在MSVC环境下无法编译通过
最小复现代码
// OptionalSeriealisationTest.cpp //#define JSON_USE_IMPLICIT_CONVERSIONS 0 // 曾尝试按issue讨论切换该宏开关,无效果 #include <iostream> #include <optional> #include <nlohmann/json.hpp> namespace nlohmann { template <typename T> struct adl_serializer<std::optional<T>> { // 序列化逻辑为报错根源 static void to_json(json& j, const std::optional<T>& opt) { if (opt) { j = opt.value(); } else { j = nullptr; } } static void from_json(const json& j, std::optional<T>& opt) { if (j.is_null()) { opt = std::nullopt; } else { opt = j.get<T>(); } } }; } using json = nlohmann::ordered_json; int main() { { // 反序列化正常值:可正常运行 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; } { // 反序列化空值:可正常运行 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::optional<float> x = 4.0; json j; j["x"] = x; } { // 编译报错 std::optional<float> x = 4.0; auto j = json({ "x", x }); } }
MSVC编译报错
Error E0289: no instance of constructor "nlohmann::basic_json<...>::basic_json" matches the argument list [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>>]
错误原因
核心问题是adl_serializer特化时绑定的json类型,与实际使用的json类型不匹配:
- 你在
nlohmann命名空间内编写特化逻辑时,函数参数里写的json是库默认提供的nlohmann::json类型——即basic_json使用默认模板参数(对象存储用std::map)的实例化版本。 - 你的代码里实际用的是
nlohmann::ordered_json,这是basic_json使用ordered_map作为对象存储的另一个独立实例化类型,和默认的nlohmann::json是完全不同的类型。 - 官方
boost::optional示例全程使用默认的nlohmann::json类型,不存在类型不匹配问题,因此可以正常编译。 - 反序列化逻辑看似正常运行,是因为反序列化调用路径上的模板推导触发了隐式转换,刚好可以命中你写的特化逻辑;但序列化时的赋值操作要求参数类型精确匹配,找不到对应
to_json重载时,编译器会尝试直接用std::optional<float>构造json对象,最终触发“无匹配构造函数”的错误。
修复方案
推荐使用通用写法,不硬编码具体json类型,让特化逻辑兼容所有basic_json实例(包括默认json、ordered_json和自定义参数的basic_json),修正后的adl_serializer代码如下:
namespace nlohmann { template <typename T> struct adl_serializer<std::optional<T>> { template<typename JsonType> static void to_json(JsonType& j, const std::optional<T>& opt) { if (opt.has_value()) { j = *opt; } else { j = nullptr; } } template<typename JsonType> static void from_json(const JsonType& j, std::optional<T>& opt) { if (j.is_null()) { opt = std::nullopt; } else { opt = j.template get<T>(); } } }; }
替换原有特化代码后即可正常编译运行,无需修改其他业务逻辑。
如果你的项目里只会使用ordered_json一种json类型,也可以直接把特化逻辑里的json参数类型替换为nlohmann::ordered_json,但这种写法通用性差,不推荐。
内容的提问来源于stack exchange,提问作者AcountForDaftQuestions
相关产品推荐
相关产品推荐

