ARTICLE DETAIL

资讯详情

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

JSON for Modern C++ 解析回调详解:深入理解 json_sax::null 的触发时机与返回语义

JSON for Modern C++ 解析回调详解:深入理解 json_sax::null 的触发时机与返回语义 JSON for Modern C 解析回调详解深入理解 json_sax::null 的触发时机与返回语义【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json本文围绕 JSON for Modern Cnlohmann/json单头文件 C 库的 SAX 解析接口详解json_sax::null这一事件回调的声明语义、触发时机、返回值约定及其在事件流中的位置。文中以官方参考文档 docs/mkdocs/docs/api/json_sax/null.md 为核心骨架并结合include/nlohmann/detail/input/json_sax.hpp、parser.hpp、官方示例sax_parse.cpp与对应测试源码帮助读者掌握使用json::sax_parse实现流式事件驱动解析null值的方法以及将 SAX 事件接入自定义消费者类时的正确姿势。接口声明与基本语义json_sax::null是 SAX 事件处理器中专门对应 JSONnull字面量的纯虚回调其声明位于 docs/mkdocs/docs/api/json_sax/null.md 中virtual bool null() 0;触发条件解析过程中读到了一个null值。参数无。null在 JSON 中不携带任何附加数据因此该回调不像boolean(bool)、number_integer(...)、string(...)那样带有值参数。返回值bool表示解析是否应继续进行whether parsing should proceed。该声明在源码中的实体位于 include/nlohmann/detail/input/json_sax.hpp#L44。由于json_sax是一个全部由纯虚函数构成的接口结构体并显式默认了构造/拷贝/移动/析构等特殊成员函数任何希望消费 SAX 事件的类都必须实现包括null()在内的全部 14 个回调无法省略。如果只是想验证输入是否为合法 JSON 而不关心具体值可以继承库中现成的json_sax_acceptor其null()直接返回true如果希望在解析的同时构建出nlohmann::json值库内部则使用json_sax_dom_parser。null 事件在何时被抛出文本 JSON 解析路径在文本解析器 include/nlohmann/detail/input/parser.hpp#L294-L301 中当词法器lexer报告token_type::literal_null时解析器会立即调用该回调并根据返回值决定是否提前终止解析case token_type::literal_null: { if (JSON_HEDLEY_UNLIKELY(!sax-null())) { return false; } break; }也就是说null字面量被识别后库并不为它分配任何值存储而是纯粹通过null()事件把“读到 null”这一事实通知给消费者。二进制格式解码路径同样的事件还会在库所支持的各类二进制序列化格式BSON、CBOR、MessagePack、UBJSON、BJData 等的解码过程中被触发。在二进制读取器 include/nlohmann/detail/input/binary_reader.hpp 中存在多处直接调用sax-null()的代码点例如其中 L355、L927、L1538、L2470 附近分别对应不同二进制格式里的 null 标记如 CBOR 的0xf6、MessagePack 的0xc0等。这说明无论输入是 JSON 文本还是二进制变体只要语义上等价于 null 的数据被读取最终都会汇聚到同一个null()回调上便于上层统一处理。嵌套结构中的位置null可以出现在 JSON 文档的任何值位置作为根值、数组元素或对象成员值。从事件流的视角看null()回调总是在紧邻的结构事件之后被调用例如在对象中它出现在对应key(...)事件之后。这一顺序在官方示例的输出中可以得到直观验证。返回值的真实作用控制解析是否继续null()返回bool其含义与 SAX 接口中所有其他回调一致返回true表示“继续解析”返回false表示“停止解析”。这一点由两处共同保证接口定义注释明确写明了这一约定参见 include/nlohmann/detail/input/json_sax.hpp#L40-L44文本解析器中通过JSON_HEDLEY_UNLIKELY(!sax-null())检查返回值一旦为falseparse_value立即返回false并向上传播最终使整个sax_parse返回false。利用这一机制可以实现的典型场景包括提前短路early termination——例如在某个键读到null时决定不必继续解析整个大文档从而节省时间与内存。注意在回调内部不应抛出异常与返回false混用官方内置实现的做法是parse_error回调在allow_exceptions打开时抛异常否则返回false而普通值事件回调包括null()通常返回true表示照常继续。从源码看 null 值的处理实现虽然null()是纯虚函数、由使用者实现但仓库提供了两个内置实现用于理解“解析 null 到底做了什么”DOM 构建路径json_sax_dom_parser::null()调用了handle_value(nullptr)从而在正在构建的basic_json值中创建一个类型为value_t::null的节点然后返回true见 include/nlohmann/detail/input/json_sax.hpp#L193-L197。handle_value会依据当前引用栈状态栈空则成为根节点、栈顶为数组则追加、为对象则写入该 key 对应槽位把新 null 值挂到正确位置。也就是说经典json::parse得到的null就是经由这条json_sax_dom_parser链构造出来的。校验路径json_sax_acceptor::null()仅返回true不做任何存储用于json::accept这类“只验证合法性”的场景。值得补充的是在启用诊断位置JSON_DIAGNOSTIC_POSITIONS的构建下json_sax_dom_parser还会为 null 值记录起点/终点由于 JSON 的null字面量恰好是 4 个字符代码按v.start_position v.end_position - 4推算其在源文本中的位置见 include/nlohmann/detail/input/json_sax.hpp#L364-L369 的注释说明。此外SAX 接口的类型检测工具 include/nlohmann/detail/meta/is_sax.hpp#L24 通过null_function_t来约束“某个类型是否可用作 SAX 处理器”这也表明null()是判定 SAX 兼容性的必需成员之一。完整可运行的 SAX 事件消费示例参考文档直接引用官方示例 docs/mkdocs/docs/examples/sax_parse.cpp下面还原其核心结构并标注与null()相关的要点#include iostream #include iomanip #include sstream #include nlohmann/json.hpp using json nlohmann::json; // 一个简单的事件消费者把传入的值收集为字符串继承 json::json_sax_t // 可以借助基类接口避免遗漏某个必须实现的回调 class sax_event_consumer : public json::json_sax_t { public: std::vectorstd::string events; bool null() override { events.push_back(null()); return true; // 返回 true继续解析 } // ... 其余 boolean/number/string/start_object/end_object/ // start_array/end_array/key/binary 事件全部 override ... bool parse_error(std::size_t position, const std::string last_token, const json::exception ex) override { events.push_back(parse_error(position std::to_string(position) , last_token last_token ,\n ex std::string(ex.what()) )); return false; // 出错时返回 false终止解析 } }; int main() { auto text R({ Image: { ... DeletionDate: null, ... } }); sax_event_consumer sec; bool result json::sax_parse(text, sec); for (auto event : sec.events) { std::cout event \n; } std::cout \nresult: std::boolalpha result std::endl; }其中与null()直接相关的两行是输入 JSON 片段里的DeletionDate: null以及事件消费者中对null()的重写。入口json::sax_parse(text, sec)是驱动整个解析过程的函数可参考其 API 文档 docs/mkdocs/docs/api/basic_json/sax_parse.md。运行输出解读null 在事件流中的精确位置官方示例的完整输出保存在 docs/mkdocs/docs/examples/sax_parse.output 中节选关键片段start_object(elements18446744073709551615) key(valImage) start_object(elements18446744073709551615) key(valWidth) number_unsigned(val800) ... key(valAnimated) boolean(valfalse) key(valIDs) start_array(elements18446744073709551615) number_unsigned(val116) number_unsigned(val943) number_unsigned(val234) number_integer(val-38793) end_array() key(valDeletionDate) null() // -- null 字面量对应的事件 key(valDistance) number_float(val12.723375, s12.723374634) end_object() end_object() parse_error(position460, last_token12.723374634U000A }U000A }], ex[json.exception.parse_error.101] parse error at line 17, column 6: syntax error while parsing value - unexpected ]; expected end of input) result: false观察这段输出可以总结出几点重要结论事件顺序null()与key(valDeletionDate)相邻发生在对象结构内部紧随键事件之后说明 null 被当作一个普通的对象成员值事件上报输出中的elements18446744073709551615这是std::size_t的最大值对应detail::unknown_size()的编码——文本格式在读完对象/数组前无法预知元素个数于是用该哨兵值表示“未知”。这与 SAX 接口文档中“二进制格式可能上报元素个数”的说明一致因为如 CBOR 等格式可以在结构头中携带确定的长度信息result: false示例输入文本在结尾处多包了一层数组括号...}]后还有一个]导致解析在12.723374634之后遇到非预期的]而触发parse_error事件消费者记录错误并返回falsesax_parse随即以false结束。也就是说示例前面记录的null()事件本身都执行成功解析的失败发生在更后面的语法错误上——这恰好演示了null()返回值true让解析得以一路推进到错误点也演示了parse_error返回false时整个解析如何终止。与测试代码的相互印证仓库测试对null()事件有着大量断言可以进一步佐证本篇文章对触发时机与输出格式的解读tests/src/unit-class_parser.cpp#L33 与 tests/src/unit-deserialization.cpp#L33 中测试用 SAX 事件收集器同样把 null 事件记录为null()字符串与官方示例的输出格式完全一致tests/src/unit-deserialization.cpp#L1111-L1130 附近的断言给出了完整事件序列样例如string(string), number_float(43.12), end_array(), null(), ...可用于校验自定义消费者实现时 null 在流中的位置是否符合预期。版本与适用范围json_sax接口含null()自 3.2.0 版本起引入参见 docs/mkdocs/docs/api/json_sax/index.md 的版本历史说明当前仓库对应版本为 3.12.0见 include/nlohmann/detail/input/json_sax.hpp#L3 的文件头。null()适用于所有走 SAX 接口的解析入口包括文本解析与二进制格式解码在实现自己的 SAX 处理器时只要明确“读到 null 后是否需要继续”即可正确返回布尔值。小结json_sax::null虽然是一个没有参数、实现极简的回调却是 SAX 事件流中处理 JSONnull的唯一入口。理解它的触发时机词法器识别literal_null后立即调用、返回语义true继续 /false终止以及它在官方 DOM 构建器与 acceptor 中的两种内置实现有助于读者写出健壮的流式解析器并在需要提前中断解析或逐事件过滤数据的场景下做出正确决策。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表