首页
/ JSON for Modern C++ 类型检查:深入解析 nlohmann::basic_json 的 is_number() 接口

JSON for Modern C++ 类型检查:深入解析 nlohmann::basic_json 的 is_number() 接口

2026-09-07 14:41:13作者:宣海椒Queenly

导读

nlohmann/json(JSON for Modern C++) 中,basic_json 实例可以动态承载 null、布尔、有符号整数、无符号整数、浮点数、对象、数组、字符串与二进制等多种 JSON 值。is_number() 是这套类型检查家族中最常用的入口之一,用于判断当前值是否为「数字」——包括有符号整数、无符号整数与浮点数三种底层类型。读完本文你将掌握 is_number() 的完整语义、与 is_number_integer() / is_number_unsigned() / is_number_float() 的区分关系、底层实现原理,以及如何结合运行示例与单元测试在真实 C++ 代码中正确地用它守卫数据分支。

函数签名与语义

is_number() 的完整定义位于 include/nlohmann/json.hppbasic_json 类型检查(inspection)区段:

constexpr bool is_number() const noexcept;

该函数返回 true 当且仅当 JSON 值是数字,且涵盖三类底层存储

  • 有符号整数(对应内部标签 value_t::number_integer);
  • 无符号整数(对应内部标签 value_t::number_unsigned);
  • 浮点数(对应内部标签 value_t::number_float)。

与之相对,false 意味着该值是 null、布尔、字符串、对象、数组或二进制数据等其他类型。

返回值、异常安全与复杂度

维度 结论
返回值 类型为数字时(不论有符号整数、无符号整数还是浮点)返回 true,否则返回 false
异常安全 No-throw guarantee,任何情况下都不会抛出异常
复杂度 常数时间 O(1),只做若干枚举比较

函数声明为 constexprnoexcept,意味着它既可以在常量表达式上下文中使用,也可以放心地出现在不可能抛出异常的关键路径上,例如无异常(-fno-exceptions)的嵌入式环境。这一点在 include/nlohmann/json.hpp 中整个类型检查函数族(is_nullis_booleanis_numberis_arrayis_stringis_binary 等)都保持了一致的设计约定。

底层实现:一次枚举标签的复合判定

阅读 include/nlohmann/json.hpp 可以发现,is_number() 的实现本身只是一个复合谓词:

constexpr bool is_number() const noexcept
{
    return is_number_integer() || is_number_float();
}

而它的两个组成部分分别是独立的枚举比较(include/nlohmann/json.hpp):

constexpr bool is_number_integer() const noexcept
{
    return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned;
}

constexpr bool is_number_unsigned() const noexcept
{
    return m_data.m_type == value_t::number_unsigned;
}

constexpr bool is_number_float() const noexcept
{
    return m_data.m_type == value_t::number_float;
}

可以看到,所有判定最终都归结为对 m_data.m_type(即 value_t 枚举 中的类型标签)的一次或多次等值比较,没有任何动态分配、字符串解析或遍历开销,这正是「常数复杂度 + 不抛异常」的来源。从类型覆盖面推导出的等价关系如下:

  • is_number() 的覆盖范围 = is_number_integer()is_number_float()
  • is_number_integer() 的覆盖范围 = is_number_unsigned() ∪(纯有符号整数);
  • is_number_integer() 蕴含 is_number()is_number_unsigned() 蕴含 is_number_integer()

重要:解析器不会把带小数的字面量视为整数

需要注意一点:判定结果取决于值在构造或解析后最终落入的底层标签。例如解析 "1.0" 这样的 JSON 文本时会得到 number_float 标签,因此 is_number()is_number_float() 都为 true,但 is_number_integer()false。若你需要「整数值」语义(例如作为数组下标或计数),应使用 is_number_integer()is_number_unsigned() 而非笼统的 is_number(),这一点在类型守卫时极易踩坑。

覆盖全部 JSON 类型的完整运行示例

仓库在 docs/mkdocs/docs/examples/is_number.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_number()
    std::cout << std::boolalpha;
    std::cout << j_null.is_number() << '\n';
    std::cout << j_boolean.is_number() << '\n';
    std::cout << j_number_integer.is_number() << '\n';
    std::cout << j_number_unsigned_integer.is_number() << '\n';
    std::cout << j_number_float.is_number() << '\n';
    std::cout << j_object.is_number() << '\n';
    std::cout << j_array.is_number() << '\n';
    std::cout << j_string.is_number() << '\n';
    std::cout << j_binary.is_number() << '\n';
}

对应输出记录在 docs/mkdocs/docs/examples/is_number.output

false
false
true
true
true
false
false
false
false

逐行解读:

取值 底层标签 is_number()
nullptr(默认构造) value_t::null false
true value_t::boolean false
17 value_t::number_integer true
12345678987654321u value_t::number_unsigned true
23.42 value_t::number_float true
{"one",1},{"two",2} value_t::object false
{1,2,4,8,16} value_t::array false
"Hello, world" value_t::string false
json::binary({1,2,3}) value_t::binary false

需要注意示例中无符号整数采用 12345678987654321u(超过 64 位有符号整数范围的场景)来确保字面量落入无符号分支;对同样可被有符号整数容纳的较小无符号值,构造时仍可能被归入 number_integer,判定行为由实际标签决定。

编译运行方式

将上述源文件与本仓库头文件结合编译即可,头文件采用单头分发模式,最简单的方式是使用 single_include/nlohmann/json.hpp

g++ -std=c++11 -I single_include is_number.cpp -o is_number
./is_number

工程支持 C++11 及以上标准;若你使用 CMake,也可通过 Makefile 或仓库根目录的 CMakeLists.txt 引入,并在 target_link_libraries 中链接 nlohmann_json::nlohmann_json 目标。

单元测试视角:与全部 is_* 兄弟函数的联动验证

is_number() 的行为并非孤立存在,它与整个类型检查函数族相互印证。仓库的检查测试位于 tests/src/unit-inspection.cpp,其中的 "convenience type checker" 区段针对对象、数组、null、布尔、字符串、三类数字、二进制、discarded 等每种标签,同时断言全部 is_* 谓词的结果,确保各个标签的判定互斥且完备。

例如针对三种数字标签的核心断言(tests/src/unit-inspection.cpp):

SECTION("number (integer)")
{
    json const j(42);
    CHECK(j.is_number());
    CHECK(j.is_number_integer());
    CHECK(!j.is_number_unsigned());
    CHECK(!j.is_number_float());
}

SECTION("number (unsigned)")
{
    json const j(42u);
    CHECK(j.is_number());
    CHECK(j.is_number_integer());
    CHECK(j.is_number_unsigned());
    CHECK(!j.is_number_float());
}

SECTION("number (floating-point)")
{
    json const j(42.23);
    CHECK(j.is_number());
    CHECK(!j.is_number_integer());
    CHECK(!j.is_number_unsigned());
    CHECK(j.is_number_float());
}

而针对对象、数组、null、布尔、字符串与二进制等非数字标签的测试段(tests/src/unit-inspection.cpp)则一致地断言 CHECK(!j.is_number()),例如 json const j {"foo", 1, 1u, 42.23, false}(数组整体)不为数字。这些测试可以在构建测试目标后运行(仓库使用 doctest 框架),是验证本文结论最直接的可执行证据。

典型实战用法

由于 JSON 文本中的数字在解析后可能落入三种标签之一,编写「数字」处理逻辑时应优先采用如下守卫模式:

if (j.is_number())
{
    // 数字:可能在 number_integer / number_unsigned / number_float 中
    // 若需要统一计算,可用 get<double>() 等做转换后再运算
}
else
{
    // 处理 null/boolean/string/object/array/binary 等非数字分支
}

如需更细的语义区分,可以按精度需求分级:

if (j.is_number_unsigned())        // 无符号整数:保证非负
{
    // 例如作为索引或计数使用
}
else if (j.is_number_integer())    // 有符号整数
{
    // 例如 ID、枚举等整型字段
}
else if (j.is_number_float())      // 浮点数
{
    // 例如比率、坐标等小数场景
}
else
{
    // 非数字,做类型错误处理
}

值得留意的是 12345678987654321u 这类超出 64 位有符号范围、又小于 64 位无符号范围上限的字面量,若不显式加 u 后缀将无法直接放入 basic_json;这种「整数溢出保护」的构造语义使得 is_number() 家族在无符号大数场景下更加重要。

版本历史与演进

根据 API 文档的 version history:

  • 1.0.0:函数随库首次发布加入;
  • 2.0.0:语义扩展——在此版本之前,无符号整数需要先经转换才能判定为数字,2.0.0 起 is_number() 对无符号整数同样返回 true,与文档中「涵盖有符号、无符号整数及浮点」的现行语义一致。

is_number() 属于 basic_json 完整类型检查 API 的一部分,常用 API 文档页面包括:

若需在 C++11 及以上的任意标准、单头引入或 CMake/模块等不同分发方式下使用该 API,可以分别参考 single_include 单头文件、根目录 CMakeLists.txt模块实现

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388