ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSON for Modern C++ 中 adl_serializer::to_json 深度解析:自定义类型序列化的实现机制

JSON for Modern C++ 中 adl_serializer::to_json 深度解析:自定义类型序列化的实现机制 JSON for Modern C 中 adl_serializer::to_json 深度解析自定义类型序列化的实现机制【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json本文围绕 to_json API 文档 展开系统讲解nlohmann::adl_serializer::to_json的函数签名、参数语义与标准示例并结合仓库源码adl_serializer.hpp、to_json.hpp剖析它在basic_json构造流程中的完整调用链、SFINAE 与 ADL 查找机制以及库内置的各类型to_json重载矩阵。读完后你可以为自己的结构体正确编写to_json函数并理解json j myObj;这行赋值背后究竟发生了什么。API 声明与函数定位to_json.md 给出的接口声明如下templatetypename BasicJsonType, typename TargetType ValueType static auto to_json(BasicJsonType j, TargetType val) noexcept( noexcept(::nlohmann::to_json(j, std::forwardTargetType(val)))) - decltype(::nlohmann::to_json(j, std::forwardTargetType(val)), void())文档原文说明This function is usually called by the constructors of the basic_json class该函数通常由basic_json类的构造函数调用。即它是用户自定义类型 → JSON 值这一方向上的官方入口配合 basic_json 构造函数 完成隐式或显式的类型转换。参数说明参数方向说明jout待写入的 JSON 值目标对象valin待读取的源值任何可序列化类型版本历史该接口自2.1.0 版本引入当前仓库源码头部标注的版本为3.12.0见 adl_serializer.hpp 文件头注释声明与文档一致。源码实现一个薄薄的前向层在 adl_serializer.hpp 中adl_serializer是一个两参数模板结构体第二参数为void风格的扩展位其to_json静态成员函数体只有一行/// brief convert any value type to a JSON value templatetypename BasicJsonType, typename TargetType ValueType static auto to_json(BasicJsonType j, TargetType val) noexcept( noexcept(::nlohmann::to_json(j, std::forwardTargetType(val)))) - decltype(::nlohmann::to_json(j, std::forwardTargetType(val)), void()) { ::nlohmann::to_json(j, std::forwardTargetType(val)); }从源码结构看这段实现有三个关键设计点限定查找qualified lookup函数体中调用的是::nlohmann::to_json(...)而非裸的to_json(...)。这保证了先检查nlohmann命名空间中库提供的内置to_json重载即 detail/conversions/to_json.hpp 中定义的那一整组若没有可用的库内重载ADL基于参数类型的查找会继续在你自己定义的命名空间中查找同名函数。这正是ADL Serializer名称的由来。SFINAE 化的返回类型- decltype(::nlohmann::to_json(...), void())使得目标类型不可序列化在编译期表现为没有匹配的函数从而让编译器回退到basic_json的其他构造函数或报出清晰的错误而不是在函数体内部触发硬错误。异常说明的传播noexcept(noexcept(...))把被调用方to_json的异常安全性原样转发方便库内部在 noexcept 上下文中做决策。库内to_json的对象式函数内置重载最终汇聚到一个to_json_fn结构体上to_json.hppstruct to_json_fn { templatetypename BasicJsonType, typename T auto operator()(BasicJsonType j, T val) const noexcept(noexcept(to_json(j, std::forwardT(val)))) - decltype(to_json(j, std::forwardT(val)), void()) { return to_json(j, std::forwardT(val)); } };并通过static_const暴露为constexpr const auto to_json。源码注释特别指出在 C17 之前这个constexpr变量需要放在匿名命名空间中引用了 Open-Standard 的 N4381 提案来解释原因因此#ifndef JSON_HAS_CPP_17分支会额外包裹一层匿名命名空间to_json.hpp。内置重载覆盖了哪些类型to_json.hpp 中的enable_if_t条件重载矩阵决定了哪些类型不需要用户自己写to_json就能直接转成 JSON源类型JSON 目标类型源码位置约bool含std::vectorbool::reference特例booleanto_json.hpp兼容字符串类型std::string、char*等stringto_json.hpp浮点类型number_floatto_json.hpp无符号 / 有符号整数number_unsigned / number_integerto_json.hpp枚举类型可用JSON_DISABLE_ENUM_SERIALIZATION关闭底层整型to_json.hpp数组容器、C 数组、std::valarray、C20 range viewarrayto_json.hpp对象容器key-value 对objectto_json.hppstd::pairT1,T2两元素 arrayto_json.hpp元组类型array按元素展开to_json.hppstd::optionalTC17有值则解包否则 nullto_json.hppstd::filesystem::path可用时stringUTF-8to_json.hpp二进制容器类型binaryto_json.hpp上表正是adl_serializer::to_json被调用前库先尝试内置重载的完整依据只要你的类型能落入其中一行basic_json构造函数就会直接走external_constructor完成赋值不会触碰用户代码。完整示例为用户自定义类型实现 to_json以下示例完整继承自 to_json.md 文档对应仓库中的 to_json.cpp#include iostream #include nlohmann/json.hpp using json nlohmann::json; namespace ns { // a simple struct to model a person struct person { std::string name; std::string address; int age; }; } // namespace ns namespace ns { void to_json(json j, const person p) { j json{ {name, p.name}, {address, p.address}, {age, p.age} }; } } // namespace ns int main() { ns::person p {Ned Flanders, 744 Evergreen Terrace, 60}; json j p; std::cout j std::endl; }运行输出见 to_json.output{address:744 Evergreen Terrace,age:60,name:Ned Flanders}示例逐段解读to_json必须定义在person所在的命名空间ns中。文档原文强调这个函数在basic_json(ns::person)构造函数被触发时由adl_serializer调用。调用路径是json j p;→basic_json的兼容类型构造函数 →adl_serializerns::person::to_json(*this, p)→ 限定查找失败后ADL 在ns命名空间中找到你写的to_json。如果把函数写到main命名空间ADL 将找不到它编译期 SFINAE 会直接排除这条转换路径。函数签名约定第一个参数是json或更一般的BasicJsonType第二个参数是源对象。示例用const person这是最常见的形式由于adl_serializer::to_json的TargetType val是转发引用const 左值、右值、非 const 左值均可正确传递。函数体就是普通的赋值构造j json{{...}};构造一个对象字面量。注意输出中键的顺序是address、age、name——对象字面量中的书写顺序被洗牌了这是basic_json的对象内部按std::map语义键字典序存储所致与插入顺序无关。反向操作如果还需要从 JSON 还原出person请实现对应命名空间下的from_json其文档见 from_json.md。adl_serializer结构体中同样定义了from_json的两个静态成员含identity_tag版本用于推导返回值机制与to_json完全对称adl_serializer.hpp。验证方式与延伸阅读单元测试用户自定义类型UDT序列化行为的回归验证集中在 unit-udt.cpp其中覆盖了to_json/from_json宏与非侵入式函数两种写法。示例源码与预期输出成对存放于 docs/mkdocs/docs/examples/ 目录本文引用的 to_json.cpp 与 to_json.output 即其中之一。单头文件使用者可直接包含 single_include/nlohmann/json.hpp多文件集成则包含 include/nlohmann/adl_serializer.hpp 所在的模块化头文件。小结adl_serializer::to_json本身只是一个转发 SFINAE的静态成员真正的工作分两部分库内置的nlohmann::to_json重载族负责标准类型见上文矩阵而 ADL 机制负责在用户命名空间中寻找自定义类型的to_json。编写序列化函数时只需记住两条纪律定义在类型所在命名空间、签名首参为json即可让basic_json构造函数自动完成对接。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表