首页
/ JSON for Modern C++ 类型体系解析:nlohmann::basic_json::value_t 从定义、比较规则到实战

JSON for Modern C++ 类型体系解析:nlohmann::basic_json::value_t 从定义、比较规则到实战

2026-09-07 16:48:02作者:郁楠烈Hubert

value_t 是 nlohmann/json(JSON for Modern C++)内部用来统一标识所有 JSON 值类型的基础枚举,它是 type() 查询函数与全部 is_* 类型检查函数的判定依据。本文以官方 API 文档 docs/mkdocs/docs/api/basic_json/value_t.md 为主线,结合 include/nlohmann/detail/value_t.hpp 等源码实现,完整讲解枚举定义、类型顺序与比较运算符重载、数字三分类的设计动机,并给出可直接运行的类型查询示例,帮助你在处理 JSON 值时准确判断其类型并正确使用比较语义。

value_t 枚举定义与 10 个类型标识符

官方文档给出的完整定义如下:

enum class value_t : std::uint8_t {
    null,
    object,
    array,
    string,
    boolean,
    number_integer,
    number_unsigned,
    number_float,
    binary,
    discarded
};

该枚举在源码中定义于 include/nlohmann/detail/value_t.hpp,底层宽度为 std::uint8_t。每个枚举项的含义可由源码注释直接确认:

枚举值 含义
null null 值
object 对象(无序的键值对集合)
array 数组(有序的值集合)
string 字符串值
boolean 布尔值
number_integer 数字(有符号整数)
number_unsigned 数字(无符号整数)
number_float 数字(浮点数)
binary 二进制数组(有序字节集合)
discarded 被解析器回调函数丢弃的值

value_t 是整个库区分子类型(distinguish the stored values)的内部基础:basic_json 的所有类型检查函数都建立在这一枚举之上。从源码结构看,basic_json 实例内部持有类型字段,type() 直接返回它:

// include/nlohmann/json.hpp
constexpr value_t type() const noexcept
{
    return m_data.m_type;
}

include/nlohmann/json.hpp#L1360-L1363

依赖 value_t 的类型检查函数族

文档明确指出,以下函数均依赖 value_t 进行判定:

它们在源码中的实现模式高度一致,即直接比对 m_data.m_type 与对应枚举值,例如 include/nlohmann/json.hpp#L1381-L1384

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

而两个分类函数 is_primitive()is_structured() 则是按类型集合划分的快捷判断,见 include/nlohmann/json.hpp#L1365-L1377

/// 原始类型:null、string、boolean、number、binary
constexpr bool is_primitive() const noexcept
{
    return is_null() || is_string() || is_boolean() || is_number() || is_binary();
}

/// 结构化类型:array、object
constexpr bool is_structured() const noexcept
{
    return is_array() || is_object();
}

值得注意的是 discarded:它表示被解析器回调函数(parser callback)丢弃的值。从源码结构可以印证,各 sax_parse 实现中,当回调拒绝某个元素时,库会用 basic_json(value_t::discarded) 构造占位结果(如 single_include/nlohmann/json.hpp#L25831),因此处理大型 JSON 流式解析时,is_discarded() 是检查“某部分是否被回调丢弃”的手段。

数字为何分为三种枚举值

文档特别指出,数字有三个枚举项,目的是区分不同的数字底层类型,对应的类型别名为:

这一设计保证了整数精确性:只要整数在范围内,nlohmann::json 就按 number_integer / number_unsigned 存储而不转成浮点;只有真正的浮点字面量或无法容纳的整数才落入 number_float。这也是文档示例中 -1742u23.42 三个值能分别命中三种类型的原因。

类型顺序(Ordering)与比较运算符重载

文档中的 Ordering 说明给出了类型比较的既定顺序:

  1. null
  2. boolean
  3. number_integernumber_unsignednumber_float
  4. object
  5. array
  6. string
  7. binary

其中 discarded 不参与任何排序(unordered)。这个顺序在源码中通过一张查找表实现,见 include/nlohmann/detail/value_t.hpp#L86-L104

static constexpr std::array<std::uint8_t, 9> order = {{
        0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
        1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
        6 /* binary */
    }
};

注意数组下标即枚举的整数值,而 discarded(下标 9)不在数组范围内,因此比较结果直接为 unordered / false。源码注释还说明了该顺序的设计参考:与 Python 的类型序一致(null < boolean < number < object < array < string < binary),并且刻意不将 binarystring 直接可比,避免 JSON 文件中出现令人意外行为。

比较运算符的重载形式由 JSON_HAS_THREE_WAY_COMPARISON 宏决定:C++20 及以上编译时提供 operator<=>,否则提供 operator<,见 include/nlohmann/detail/value_t.hpp#L80-L104

文档给出的比较运算符警告同样值得重视:operator< 和 C++20 起的 operator<=> 按上述“类型序”比较,而其余关系/相等运算符(如 >== 的整数序语义)在 C++20 前按各枚举项的整数值比较;由于 C++20 中各编译器对 operator<=> 生成的“rewritten candidates”处理不一致,文档建议:

  • 想按类型序比较:使用 operator<operator<=>
  • 想按枚举整数值比较:使用 operator==operator!=

源码中还有一段针对 GCC 的显式 workaround(见 include/nlohmann/detail/value_t.hpp#L106-L115),注释指出 GCC 在存在用户定义 spaceship 运算符时会优先选择内建 operator< 而非重写候选(对应 GCC bug #105200),因此额外提供了 inline bool operator<(const value_t, const value_t),内部通过 std::is_lt(lhs <=> rhs) 保证行为一致。这段实现细节正是文档中“编译器行为不一致”警告的落地证据。

按类型构造 JSON 值

value_t 文档还交叉引用了构造函数 basic_json(const value_t value_type)——“创建具有给定类型默认值的 JSON 值”。该构造函数实现见 single_include/nlohmann/json.hpp#L22200-L22206

/// create an empty value with a given type
basic_json(const value_t v)
    : m_data(v)
{
    assert_invariant();
}

这意味着可以显式声明一个“已知类型但内容为空/默认”的 JSON 值,例如 nlohmann::json j = nlohmann::json::value_t::array; 得到一个空数组类型的值。同样的模式也用于 nullptr 构造(basic_json(std::nullptr_t) 内部调用 basic_json(value_t::null),见 single_include/nlohmann/json.hpp#L22210-L22214)。

完整示例:用 type() 查询所有 JSON 类型

官方文档的示例代码位于 docs/mkdocs/docs/examples/type.cpp,它演示了 type() 如何为每种 JSON 类型返回对应的 value_t

#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 = 42u;
    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";

    // call type()
    std::cout << std::boolalpha;
    std::cout << (j_null.type() == json::value_t::null) << '\n';
    std::cout << (j_boolean.type() == json::value_t::boolean) << '\n';
    std::cout << (j_number_integer.type() == json::value_t::number_integer) << '\n';
    std::cout << (j_number_unsigned.type() == json::value_t::number_unsigned) << '\n';
    std::cout << (j_number_float.type() == json::value_t::number_float) << '\n';
    std::cout << (j_object.type() == json::value_t::object) << '\n';
    std::cout << (j_array.type() == json::value_t::array) << '\n';
    std::cout << (j_string.type() == json::value_t::string) << '\n';
}

预期输出(来自 docs/mkdocs/docs/examples/type.output)为 8 行 true,分别验证了 null、boolean、number_integer、number_unsigned、number_float、object、array、string 八种类型标识的判定均命中。

从该示例可以注意到两个实践要点:

  • json j_null; 默认构造即得到 value_t::null 类型值;
  • 字面量 42u 因带 u 后缀走无符号整型路径,故命中 number_unsigned 而非 number_integer,与前述数字三分类设计相互印证。

版本历史

按官方文档记载,value_t 的演进如下:

  • version 1.0.0 起提供该枚举;
  • version 2.0.0 新增无符号整数类型 number_unsigned
  • version 3.8.0 新增二进制类型 binary

结合本仓库源码(文件头标注 version 3.12.0)可以看到当前实现已包含全部 10 个枚举项及 C++20 三路比较支持。

小结

value_t 虽只是一个 10 项的小枚举,却是理解 nlohmann/json 类型系统的关键枢纽:type() 读取它,is_* 函数比对它,数字三分类依赖它保证整数精度,discarded 通过它支撑流式解析丢弃语义,而 operator< / operator<=> 的自定义顺序则为不同 JSON 值之间的“类型级”比较提供了可移植语义。实际开发中,建议对任何来源不明的 JSON 值先做 is_* 类型检查再取值,并按文档建议显式选择比较运算符,以避免 C++20 重写候选带来的行为差异。

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

项目优选

收起
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