ARTICLE DETAIL

资讯详情

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

nlohmann::basic_json::contains 成员函数详解:按键与 JSON Pointer 判断元素是否存在

nlohmann::basic_json::contains 成员函数详解:按键与 JSON Pointer 判断元素是否存在 nlohmann::basic_json::contains 成员函数详解按键与 JSON Pointer 判断元素是否存在【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json导读nlohmann::jsonJSON for Modern C提供的contains()是判断某个元素是否存在于当前 JSON 值中的常用成员函数。本文将基于 contains.md 完整展开它的三个重载形式按对象键检查、按可比较的异构键类型如std::string_view检查、以及按 JSON Pointer 路径检查。读完本文后你将掌握每种重载的适用场景、返回值约定、复杂度与异常保证并能结合源码理解其底层实现在写业务逻辑时用contains()安全地代替先查询再判断的繁琐写法。函数签名一览contains()是basic_json的一个const成员函数共有三个重载// (1) 以对象的标准键类型查找 bool contains(const typename object_t::key_type key) const; // (2) 以任意可与对象键类型比较的类型查找泛型重载 templatetypename KeyType bool contains(KeyType key) const; // (3) 以 JSON Pointer 查找 bool contains(const json_pointer ptr) const;三种重载分别解决三类问题按对象键检查判断 JSON 对象中是否存在键值等价于key的元素。若元素不存在或当前 JSON 值本身不是对象一律返回false。泛型键检查行为与重载 1 相同仅当KeyType可以与typename object_t::key_type比较且typename object_comparator_t::is_transparent定义了一个类型时才可用即对象比较器支持异构查找。按 JSON Pointer 检查判断给定的 JSON pointerptr能否在当前 JSON 值中被成功解析到某个存储值。实现上重载 1、2 与重载 3 走的是完全不同的两条路径前者直接查询对象的内部容器后者交给json_pointer逐段解析。在 include/nlohmann/json.hpp 中可以看到三个重载紧邻实现其中前两个基于object-find(...)比较迭代器第三个则委托给ptr.contains(this)。模板参数与各参数详解模板参数KeyType这是重载 2 独有的模板参数其约束为一个对象键类型——但不能是json_pointer——且能够使用object_comparator_t与string_t进行比较。在 C17 下它还可以是字符串视图string view。模板参数含义可用类型示例KeyType用于查找键的异构类型const char*、std::string、C17 的std::string_view等函数参数参数方向说明keyin要检查是否存在性的键值ptrin要检查是否存在性的 JSON Pointer返回值约定重载返回值(1)若存在与key等价键的元素返回true未找到该键或当前 JSON 值不是对象时返回false(2)同重载 1(3)若 JSON Pointer 可以解析到某个存储值返回true否则返回false一个容易混淆的点是对非对象类型调用按键重载不会抛异常而是安静地返回false。从源码可以印证这一点——include/nlohmann/json.hpp 的实现是bool contains(const typename object_t::key_type key) const { return is_object() m_data.m_value.object-find(key) ! m_data.m_value.object-end(); }由于is_object()检查在前且具有短路求值特性当 JSON 值是数组、字符串、数字、布尔或null时函数直接返回false根本不会去访问对象容器。这也正是官方文档 Notes 中所说的该方法在非对象的 JSON 类型上始终返回false但可以安全地在任意 JSON 值类型上调用。异常安全、异常行为与复杂度异常安全强异常安全保证——如果发生异常原 JSON 值保持完整不变。异常行为三个重载都不会主动抛出异常。但需要留意一个例外场景虽然重载 3 本身不抛异常但若传入的json_pointer中包含非法引用记号如数组索引带前导0、或索引非数字解析发生在构造 pointer 或内部解析阶段会触发parse_error详见下文示例。复杂度关于 JSON 对象大小呈对数复杂度Logarithmic in the size of the JSON object。这是因为底层实现直接复用了标准库std::map/std::mapobject_t默认基于有序关联容器的find。后置条件Postconditions官方文档给出了一条非常有实用价值的不变式若j.contains(x)对键或 JSON Pointerx返回true那么随后安全地调用j[x]是成立的。这使contains()天然适合作为operator[]/at()访问前的探路步骤避免访问不存在的路径。结合源码看实现原理前两个重载复用对象容器的find从 include/nlohmann/json.hpp 可以看到重载 1 与重载 2 的实现思路一致都归结为对内部对象容器做find并与end()比较templateclass KeyType, detail::enable_if_t detail::is_usable_as_basic_json_key_typebasic_json_t, KeyType::value, int 0 bool contains(KeyType key) const { return is_object() m_data.m_value.object-find(std::forwardKeyType(key)) ! m_data.m_value.object-end(); }重载 2 通过is_usable_as_basic_json_key_type这一 SFINAE 约束保证只有可作为 basic_json 键类型使用的类型才参与重载决议当object_comparator_t::is_transparent成立时默认对象比较器default_object_comparator_t基于std::less即透明比较器可以无缝支持std::string_view这类异构键的直接查找避免构造临时std::string。这一点在 C17 下尤为重要也是官方文档把 string view 单独列出的原因。第三个重载JSON Pointer 的逐段解析重载 3 的实现只有一行委托调用ptr.contains(this)见 include/nlohmann/json.hpp。真正的逐段解析逻辑位于 include/nlohmann/detail/json_pointer.hpp 的contains(const BasicJsonType* ptr)for (const auto reference_token : reference_tokens) { switch (ptr-type()) { case detail::value_t::object: { if (!ptr-contains(reference_token)) { return false; // 对象中未找到该键 } ptr ptr-operator[](reference_token); break; } case detail::value_t::array: { if (JSON_HEDLEY_UNLIKELY(reference_token -)) { return false; // -末尾追加标记永远无法命中已存在元素 } // ... 数字索引合法性检查失败即返回 false } ... } }从这段代码可以得出若干重要推断指针路径按reference_token分段推进任何一段在对象中找不到键或在数组中索引越界/非法都立即返回false特殊记号-RFC 6901 中表示数组末尾的追加位置在contains()中被显式判为不存在的元素——因为它永远不可能对应某个已存储的值逐段推进时当前 JSON 值若是非对象/非数组类型也会因switch落入相应分支而判定失败。这与重载 1、2 的非对象返回 false语义保持一致。测试如何验证这些行为仓库测试文件 tests/src/unit-element_access2.cpp 对contains做了系统验证对对象中存在的键断言j.contains(key) true同时覆盖const对象对不存在的键断言为false并且对所有非对象类型如null、字符串、布尔、整数、浮点等逐一构造后断言contains(foo)与contains(std::string_view(foo))均返回false。这与文档非对象返回 false、可在任意类型上调用的表述完全一致。使用示例三种重载的完整代码与输出示例 1按对象键检查重载 1以下完整示例可在 docs/mkdocs/docs/examples/contains__object_t_key_type.cpp 中查看运行输出见 contains__object_t_key_type.output#include iostream #include nlohmann/json.hpp using json nlohmann::json; using namespace nlohmann::literals; int main() { // create some JSON values json j_object R( {key: value} )_json; json j_array R( [1, 2, 3] )_json; // call contains std::cout std::boolalpha j_object contains key: j_object.contains(key) \n j_object contains another: j_object.contains(another) \n j_array contains key: j_array.contains(key) std::endl; }输出j_object contains key: true j_object contains another: false j_array contains key: false可以看到对象中存在key返回true不存在的another返回false而把同一个键拿到数组上查询时不会报错而是返回false。示例 2使用std::string_view检查重载 2C17该示例源码位于 docs/mkdocs/docs/examples/contains__keytype.c17.cpp输出见 contains__keytype.c17.output#include iostream #include string_view #include nlohmann/json.hpp using namespace std::string_view_literals; using json nlohmann::json; using namespace nlohmann::literals; int main() { // create some JSON values json j_object R( {key: value} )_json; json j_array R( [1, 2, 3] )_json; // call contains std::cout std::boolalpha j_object contains key: j_object.contains(keysv) \n j_object contains another: j_object.contains(anothersv) \n j_array contains key: j_array.contains(keysv) std::endl; }输出与示例 1 完全一致j_object contains key: true j_object contains another: false j_array contains key: false该示例需要通过JSON_HAS_CPP_17启用源文件扩展名c17即表示仅在 C17 及以上标准下编译。由于默认对象比较器支持透明查找这里无需把keysv转换为std::string即可直接完成查询。示例 3按 JSON Pointer 检查重载 3完整代码见 docs/mkdocs/docs/examples/contains__json_pointer.cpp输出见 contains__json_pointer.output#include iostream #include nlohmann/json.hpp using json nlohmann::json; using namespace nlohmann::literals; int main() { // create a JSON value json j { {number, 1}, {string, foo}, {array, {1, 2}} }; std::cout std::boolalpha j.contains(/number_json_pointer) \n j.contains(/string_json_pointer) \n j.contains(/array_json_pointer) \n j.contains(/array/1_json_pointer) \n j.contains(/array/-_json_pointer) \n j.contains(/array/4_json_pointer) \n j.contains(/baz_json_pointer) std::endl; try { // try to use an array index with leading 0 j.contains(/array/01_json_pointer); } catch (const json::parse_error e) { std::cout e.what() \n; } try { // try to use an array index that is not a number j.contains(/array/one_json_pointer); } catch (const json::parse_error e) { std::cout e.what() \n; } }输出true true true true false false false结果逐行解读JSON Pointer含义结果/number顶层键numbertrue/string顶层键stringtrue/array顶层键array数组true/array/1数组中下标 1 的元素2true/array/-记号-表示数组末尾追加位不指向已存元素false/array/4数组越界索引false/baz顶层不存在的键false值得再次强调的是虽然contains()本身不抛异常但示例中的两个try块说明当指针本身非法——例如数组索引带前导0/array/01或索引根本不是数字/array/one——会在解析阶段抛出json::parse_error。这与 include/nlohmann/detail/json_pointer.hpp 中注释标注的抛错条件parse_error.106数组索引以0开头parse_error.109数组索引不是数字一一对应。与 find / count 的关系contains()在 basic_json 的查询函数族中与find、count关系紧密find在对象中查找指定键返回指向该元素的迭代器找不到则返回end()count返回对象中某键出现的次数——对标准 JSON 对象而言结果只会是 0 或 1contains直接给出是否存在的布尔答案语义上等价于find(x) ! end()也等价于count(x) ! 0但表达意图最直接。需要区分的是contains的前两个重载只处理对象键而重载 3 接收 JSON Pointer可以对嵌套结构做多级存在性判断。后置条件保证了当contains(x)为true时j[x]无论x是键还是指针都可以安全访问因此推荐在不确定数据结构时按先contains再operator[]的模式编写健壮代码。版本历史重载加入版本后续变更(1) 标准键类型3.11.0—(2) 泛型KeyType3.6.03.11.0 起扩展为支持所有可与对象键比较的类型含std::string_view等(3) JSON Pointer3.7.0—使用建议如果你的工程尚未升级到 3.11.0则按键查找只能传std::string/const char*重载 1若需要使用字符串视图等异构键且避免临时对象构造请确保库版本不低于 3.11.0并开启 C17或相应标准的透明查找支持。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表