JSON for Modern C++ 类型检查系列:深入解析 basic_json::is_array() 判定数组类型
【免费下载链接】json JSON for Modern C++ 项目地址: https://gitcode.com/GitHub_Trending/js/json
本文围绕 JSON for Modern C++(nlohmann/json 仓库)中 nlohmann::basic_json::is_array() 成员函数展开,从函数签名、返回语义、底层实现到典型实战场景,逐层剖析「如何判断一个 JSON 值是否为数组」。读完本文,你将理解数组类型在 basic_json 内部的表示方式、is_array() 与兄弟类型检查函数(is_object、is_primitive、is_structured 等)的分工协作,并掌握在解析数据、序列化与自定义类型转换代码中安全使用类型分派的最佳实践。
函数签名与语义
is_array() 是 basic_json 公开 API 中「对象检查(object inspection)」类型检查函数家族的成员,定义如下:
constexpr bool is_array() const noexcept;
该函数返回 #!cpp true 当且仅当 JSON 值是一个数组(array),其余所有类型一律返回 false。它不修改对象状态,也不触发任何动态分配,因此非常适合在分支判断、模板分派乃至编译期上下文(constexpr)中使用。
返回值、异常安全与复杂度
| 项目 | 说明 |
|---|---|
| 返回值 | JSON 值为数组时返回 true,否则返回 false |
| 异常安全 | 强保证中的「不抛异常」:noexcept,函数体永不抛出异常 |
| 复杂度 | 常数时间 O(1),仅执行一次类型标签比较 |
由于函数被声明为 constexpr 且不抛异常,它可以在 static_assert、编译期求值以及无异常构建(如禁用 C++ 异常的环境)中使用,不会引入运行时开销。
从源码看实现原理
is_array() 的实现位于 include/nlohmann/json.hpp,完整源码如下:
/// @brief return whether value is an array
/// @sa https://json.nlohmann.me/api/basic_json/is_array/
constexpr bool is_array() const noexcept
{
return m_data.m_type == value_t::array;
}
要理解这行比较的含义,需要了解 JSON 值的类型标签体系:
-
类型枚举:库使用
detail::value_t枚举记录值的动态类型,定义在 include/nlohmann/detail/value_t.hpp,其中与数组直接相关的是array,其注释为「array (ordered collection of values)」,即值的有序集合(区别于object的「键值对无序集合」)。 -
公开别名:
basic_json将内部枚举公开为成员类型别名,见 include/nlohmann/json.hpp 中using value_t = detail::value_t;,因此用户代码可以写json::value_t::array。 -
类型标记存储:每个
basic_json对象在m_data.m_type字段(其类型即value_t)中维护自身当前的动态类型标签。is_array()本质就是一次单条比较:m_data.m_type == value_t::array。 -
底层容器:数组元素存放在指向
array_t的指针中,而array_t是模板参数ArrayType的实例化:using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;(见 include/nlohmann/json.hpp)。默认情况下ArrayType是std::vector(见 include/nlohmann/json_fwd.hpp 的前置声明),也就是标准库std::vector<basic_json>。
此外,value_t 枚举头还定义了跨类型比较的排序(null < boolean < number < object < array < string < binary),该排序与判断函数相互独立,但同属类型系统设计的一部分。
与兄弟判断函数的协同
is_array() 并非孤立存在,basic_json 为每种 JSON 类型都提供了对应的 is_xxx() 判断函数,全部集中在「object inspection」代码区(include/nlohmann/json.hpp):
is_null()、is_boolean()、is_string()、is_binary()分别判断 null、布尔、字符串、二进制数组;is_number()、is_number_integer()、is_number_unsigned()、is_number_float()判断数值的三种细分形态;is_object()与is_array()判断两种复合(结构化)类型;is_primitive()与is_structured()是更高层的聚合判断。
其中两个高层函数直接复用了 is_array():
constexpr bool is_primitive() const noexcept
{
return is_null() || is_string() || is_boolean() || is_number() || is_binary();
}
constexpr bool is_structured() const noexcept
{
return is_array() || is_object();
}
这意味着 is_array() 返回 true 时,is_structured() 必然返回 true,而 is_primitive() 必然返回 false;反之亦然。相关源码见 include/nlohmann/json.hpp。与之等价的一种写法是通过公开的 type() 函数比较:
// 以下两者语义等价
if (j.is_array()) { /* ... */ }
if (j.type() == json::value_t::array) { /* ... */ }
其中 type() 返回 value_t 枚举值,详见 type() 文档。在存在大量 switch 分派的场景下用 type() 更清晰,而在简单布尔判断场景下直接调用 is_array() 更直观。
覆盖全部 JSON 类型的完整示例
原文档提供了一个贯穿全部类型的最小示例,源码位于 docs/mkdocs/docs/examples/is_array.cpp,其可运行版本如下:
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// create JSON values
json j_null;
json j_boolean = true;
json j_number_integer = 17;
json j_number_unsigned_integer = 12345678987654321u;
json j_number_float = 23.42;
json j_object = {{"one", 1}, {"two", 2}};
json j_array = {1, 2, 4, 8, 16};
json j_string = "Hello, world";
json j_binary = json::binary({1, 2, 3});
// call is_array()
std::cout << std::boolalpha;
std::cout << j_null.is_array() << '\n';
std::cout << j_boolean.is_array() << '\n';
std::cout << j_number_integer.is_array() << '\n';
std::cout << j_number_unsigned_integer.is_array() << '\n';
std::cout << j_number_float.is_array() << '\n';
std::cout << j_object.is_array() << '\n';
std::cout << j_array.is_array() << '\n';
std::cout << j_string.is_array() << '\n';
std::cout << j_binary.is_array() << '\n';
}
运行后输出(见 docs/mkdocs/docs/examples/is_array.output):
false
false
false
false
false
false
true
false
false
对照每种输入值的判定结果如下:
| JSON 值 | 类型 | is_array() 结果 |
|---|---|---|
j_null(默认构造) | null | false |
j_boolean = true | boolean | false |
j_number_integer = 17 | number_integer | false |
j_number_unsigned_integer = 12345678987654321u | number_unsigned | false |
j_number_float = 23.42 | number_float | false |
j_object = {{"one",1},{"two",2}} | object | false |
j_array = {1,2,4,8,16} | array | true |
j_string = "Hello, world" | string | false |
j_binary = json::binary({1,2,3}) | binary | false |
需要特别留意两点边界语义:
- 二进制数据:即使底层用字节数组(
std::vector<std::uint8_t>)承载,json::binary({...})构造出的值在类型系统上属于value_t::binary而非value_t::array,因此is_array()返回false。 - 空数组:
json::array()工厂函数或{}初始化构造的空数组在类型标签上依然是value_t::array,因此is_array()返回true(这与empty()判空是两回事,见下文)。
实战要点:数组的创建与类型检查配合
明确用 json::array() 消除初始化歧义
basic_json 的初始化列表构造会根据内容自动推断类型:当列表中的每个元素都是「长度为 2、且首元素为字符串」的数组时,会被自动推断为 object,否则推断为 array(推断逻辑见 include/nlohmann/json.hpp 的初始化列表构造函数)。因此数组元素恰好是 {"key", value} 形态时容易产生歧义,应使用显式的静态工厂 json::array() 强制生成数组。
include/nlohmann/json.hpp 中 array() 的实现为:
/// @brief explicitly create an array from an initializer list
static basic_json array(initializer_list_t init = {})
{
return basic_json(init, false, value_t::array);
}
即通过第三个参数 manual_type = value_t::array 关闭类型推导,强制按数组构造。仓库测试 tests/src/unit-readme.cpp 对这一组歧义场景做了断言覆盖:
// ways to express the empty array []
json const empty_array_implicit = {{}};
CHECK(empty_array_implicit.is_array());
json const empty_array_explicit = json::array();
CHECK(empty_array_explicit.is_array());
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
CHECK(array_not_object.is_array());
CHECK(array_not_object.size() == 2);
CHECK(array_not_object[0].is_array());
CHECK(array_not_object[1].is_array());
注意 {{}} 这样的写法会生成一个空数组(这是 README 中记录的惯用法,对应 JSON 文本 []),若想要空对象应使用 json::object()。
解析结果的类型守卫
在反序列化任意输入(json::parse、流解析、_json 字面量等)时,输入根节点可能是任意类型。调用方通常先用 is_array()(常与 is_object() 组合)守卫,再安全地使用 size()、at()、迭代器或下标访问。仓库中大量测试采用了这一组合,例如:
- 二进制格式回环解析后验证根类型(tests/src/unit-cbor.cpp、tests/src/unit-msgpack.cpp、tests/src/unit-bjdata.cpp、tests/src/unit-ubjson.cpp):
CHECK((parsed.is_object() || parsed.is_array()));
- 递归遍历算法中按节点类型分派(tests/src/unit-algorithms.cpp):
if (value.is_array())
{
// 逐元素递归处理
}
这种「先 is_array()/is_object() 判断、再访问」的写法同样适用于 dump() 序列化前的类型检查、JSON Schema 式校验以及自定义 from_json/to_json 类型转换中对容器类型的判别。
对全部类型的正交断言(单元测试视角)
仓库的单元测试 tests/src/unit-inspection.cpp 在「convenience type checker」一节中,对 object、array、null、boolean、string、integer、unsigned、floating-point、binary、discarded 等全部类型逐一断言:每种类型都满足「自己的 is_xxx() 为真、其余全部为假」的正交性。例如对 array 类型:
SECTION("array")
{
json const j {"foo", 1, 1u, 42.23, false};
CHECK(!j.is_null());
CHECK(!j.is_boolean());
CHECK(!j.is_number());
// ...
CHECK(!j.is_object());
CHECK(j.is_array()); // 数组:is_array 为 true
CHECK(!j.is_string());
// ...
CHECK(!j.is_primitive()); // 数组不是原始类型
CHECK(j.is_structured()); // 数组是结构化类型
}
组合使用建议:is_array 与 empty/size
is_array() 只回答「是不是数组」,不回答「数组是否为空」。实际使用中常将其与容器接口组合:
json const data = json::parse(input);
if (data.is_array())
{
if (data.empty())
{
// 空数组 []
}
else
{
// 遍历元素:for (auto const& item : data) { ... }
}
}
else
{
// 非数组:按其他类型或错误路径处理
}
补充说明与版本信息
- 本文引用的实现位于多文件头 include/nlohmann/json.hpp 与前置声明 include/nlohmann/json_fwd.hpp;实际使用中亦可直接包含仓库提供的单头文件合并且自包含版本 single_include/nlohmann/json.hpp,无需额外依赖即可编译运行示例。
is_array()属于constexpr类型检查族,对自定义容器(如将ArrayType替换为其他模板)依旧生效,因为判定仅依赖类型标签value_t::array,与具体存储容器无关;ordered_json(基于ordered_map的类型别名)同样适用于本函数语义。- 版本历史:
is_array()自 version 1.0.0 起加入该 API,长期保持签名稳定。
相关文档索引
若需进一步了解类型系统与相邻判断接口,可在本仓库文档中继续阅读:
- 完整参考:is_array API 参考
- 类型枚举定义:value_t API 参考 与 value_t 源码
- 数组显式工厂:array API 参考
- 兄弟判断函数:is_object、is_null、is_string、is_binary、is_number、is_primitive、is_structured
- 示例与验证:完整示例源码、示例输出、类型检查单元测试、README 惯用法测试
【免费下载链接】json JSON for Modern C++ 项目地址: https://gitcode.com/GitHub_Trending/js/json
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



