首页
/ JSON for Modern C++ 类型检查:`is_binary()` 成员函数原理与实战

JSON for Modern C++ 类型检查:`is_binary()` 成员函数原理与实战

2026-09-07 22:19:00作者:彭桢灵Jeremy

本篇技术指南围绕 JSON for Modern C++(nlohmann-json)中 basic_json::is_binary() 这一成员函数展开:它用于判断当前 JSON 值是否为一个二进制数组(binary),是库在标准 JSON 七类类型之外提供的第九种扩展类型判型接口。读完本文,你将掌握 is_binary() 的声明与语义、它在 value_t 类型枚举与 basic_json 内部存储结构中的实现原理、构造 binary 值的完整姿势,以及借助单元测试洞悉其边界行为的方法。

一、函数声明与核心语义

is_binary() 的原型定义如下,直接位于 basic_json 类中:

constexpr bool is_binary() const noexcept;

该函数返回 true 当且仅当 JSON 值是一个二进制数组(binary array)。在 include/nlohmann/json.hpp 中,is_binary()is_null()is_boolean()is_number()is_object()is_array()is_string()is_discarded() 等一组判型函数并列(参见其附近的注释 return whether value is a binary array),构成了 basic_json 最基础的"查询当前值属于哪种类型"的接口族。

与其他判型函数一样,is_binary()constexprnoexcept 的:前者允许在常量表达式中使用,后者保证了它不会抛出任何异常,从而可以安全地出现在 noexcept 函数或异常安全的边界代码中。

二、实现原理:一次 value_t::binary 的类型标记分派

理解 is_binary() 的关键在于 basic_json 内部的类型存储设计。从源码看,每个 JSON 值内部都维护了一个 value_t 类型的类型标记成员(在 include/nlohmann/json.hpp 中表现为 m_data.m_type),所有判型函数本质上都是对该标记的一次相等比较。

is_binary() 的完整实现只有一行(见 include/nlohmann/json.hpp):

/// @brief return whether value is a binary array
/// @sa https://json.nlohmann.me/api/basic_json/is_binary/
constexpr bool is_binary() const noexcept
{
    return m_data.m_type == value_t::binary;
}

从中可以看到两条确定结论:

  1. 时间复杂度为常量:整个判断只是一次枚举值比较,不访问堆、不遍历容器,因此文档声明的复杂度是 Constant(O(1));
  2. 不会失败:函数既不分配内存也不做任何可能抛异常的操作,满足 No-throw guarantee(不抛异常保证),这正是它被标记 noexcept 的底气。

value_t::binary 是类型枚举中的第九个成员。完整枚举定义位于 include/nlohmann/detail/value_t.hpp

enum class value_t : std::uint8_t
{
    null,             ///< null value
    object,           ///< object (unordered set of name/value pairs)
    array,            ///< array (ordered collection of values)
    string,           ///< string value
    boolean,          ///< boolean value
    number_integer,   ///< number value (signed integer)
    number_unsigned,  ///< number value (unsigned integer)
    number_float,     ///< number value (floating-point)
    binary,           ///< binary array (ordered collection of bytes)
    discarded         ///< discarded by the parser callback function
};

binary 被注释为 "binary array (ordered collection of bytes)",即字节的有序集合。值得注意的是,该枚举头部注释明确指出:is_null()is_object()is_array()is_string()is_boolean()is_number() 系列乃至 is_discarded() 都依赖这一类型标记体系(include/nlohmann/detail/value_t.hpp),而 is_binary() 正是同一套机制对扩展类型的自然延伸。

三、binary 类型从何而来:标准 JSON 之外的类型扩展

需要先明确一个前提:binary 并非标准 JSON 规范中的类型。标准 JSON 只有 null、布尔、数字、字符串、数组、对象六类(库内部进一步把数字拆成 number_integernumber_unsignednumber_float 三种存储形式)。binary 是 JSON for Modern C++ 提供的一种扩展,其典型用途是承载 CBOR、MessagePack、UBJSON、BSON、BJData 等二进制序列化格式中的字节负载,保证这些格式与 JSON 值之间的往返不失真。

binary 值对应的存储类型是 binary_t。参考示例 docs/mkdocs/docs/examples/binary_t.cpp 可以确认其别名关系:

using json = nlohmann::json;
// binary_t 即 byte_container_with_subtype<std::vector<std::uint8_t>>
std::cout << std::is_same<nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>,
                          json::binary_t>::value << std::endl; // true

也就是说,binary_t 在底层是 byte_container_with_subtype<std::vector<std::uint8_t>>——一个字节容器再附带一个可选的 "subtype"(子类型编号,常用于描述字节流的语义,如 CBOR 标签)。该容器类定义于 include/nlohmann/byte_container_with_subtype.hpp,提供 subtype()set_subtype()has_subtype()clear_subtype() 等成员用于管理子类型。

在代码中构造 binary 值有两种常见途径:

  • 通过 json::binary(...) 工厂函数显式创建;
  • 在解析带二进制语义的格式(如 CBOR)时由解析器自动产生。

因此 is_binary() 的典型应用场景是:在一个可能混合了多种格式解析结果的容器中,先判定某个元素是否是 binary,再决定用文本型 API 还是 get_binary() 这类二进制 API 访问它

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

文档对本函数的三个契约给出明确说明,现结合源码逐一印证:

契约项 说明 源码依据
返回值 类型为 binary 返回 true,否则返回 false 函数体 return m_data.m_type == value_t::binary;include/nlohmann/json.hpp
异常安全 No-throw guarantee:绝不抛出异常 声明携带 noexcept,函数体内无任何可抛操作
复杂度 常量时间 O(1) 仅一次枚举成员比较,无遍历、无分配

需要特别指出:is_binary() 返回 true唯一条件就是内部标记为 value_t::binary。任何其他类型——哪怕是同样以字节为内容的普通 std::vector 序列化出的 JSON 数组——都会得到 false,因为数组始终是 value_t::array。换言之,"看起来像字节数组"不等于"是 binary 值",is_binary() 判断的是值的类型标记而非内容形态。

五、完整示例:对全部 JSON 类型逐一调用

文档给出的官方示例覆盖了库支持的所有主要值类型。完整可编译代码位于 docs/mkdocs/docs/examples/is_binary.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_binary()
    std::cout << std::boolalpha;
    std::cout << j_null.is_binary() << '\n';
    std::cout << j_boolean.is_binary() << '\n';
    std::cout << j_number_integer.is_binary() << '\n';
    std::cout << j_number_unsigned_integer.is_binary() << '\n';
    std::cout << j_number_float.is_binary() << '\n';
    std::cout << j_object.is_binary() << '\n';
    std::cout << j_array.is_binary() << '\n';
    std::cout << j_string.is_binary() << '\n';
    std::cout << j_binary.is_binary() << '\n';
}

对应的标准输出(见 docs/mkdocs/docs/examples/is_binary.output)为:

false
false
false
false
false
false
false
false
true

输出可以逐行对照解读:

  • 第 1~8 行:null、boolean、signed integer、unsigned integer、float、object、array、string 这八类值全部返回 false
  • 第 9 行:通过 json::binary({1, 2, 3}) 构造的 binary 值返回 true

示例中的几个细节值得注意:12345678987654321u 以无符号整数形式存入,属于 number_unsignedj_binary 用初始化列表 {1, 2, 3} 直接交给 json::binary,得到包含三个字节 [1, 2, 3] 的 binary 值;输出时借助 std::boolalpha 让布尔值以 true/false 文本呈现。整体说明了一个清晰的边界:在同一段代码里,is_binary() 是区分文本型 JSON 数据与二进制扩展数据的可靠探针。

六、与相关 API 的配合使用

is_binary() 通常是更完整访问流程的第一步。官方还提供了一组配套接口用于创建与读取 binary 值:

  • json::binary(vec, subtype) 工厂:用一个字节容器(如 std::vector<std::uint8_t>)创建 binary 值,第二个可选参数指定 subtype。
  • get_binary() / binary_t:以 binary_t(即 byte_container_with_subtype<std::vector<std::uint8_t>>)形式读取 binary 值的内部字节与子类型。
  • type_name():返回当前值类型的字符串名,binary 值对应的类型名即为 "binary"
  • subtype 系列成员(在 byte_container_with_subtype 上):subtype() 读取子类型、set_subtype() 设置子类型、has_subtype()/clear_subtype() 查询与清除。

组合使用的示例参见 docs/mkdocs/docs/examples/binary.cpp

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    // create a binary vector
    std::vector<std::uint8_t> vec = {0xCA, 0xFE, 0xBA, 0xBE};

    // create a binary JSON value with subtype 42
    json j = json::binary(vec, 42);

    // output type and subtype
    std::cout << "type: " << j.type_name()
              << ", subtype: " << j.get_binary().subtype() << std::endl;
}

其中 json::binary(vec, 42) 创建了一个包含四个字节 0xCA 0xFE 0xBA 0xBE、子类型编号为 42 的 binary 值。若要在此类代码中安全地执行"先判型、后访问",推荐写成:

if (j.is_binary())
{
    const auto& bytes = j.get_binary();   // 子类型与字节都可从此处取得
    // 处理二进制负载 ...
}
else
{
    // 按普通 JSON 值处理 ...
}

这样便能在不触发 type_error(例如类型不符时抛出的 302 号异常族)的前提下完成分支。

七、单元测试中的行为证据

仓库的测试套件为 is_binary() 的行为提供了充分的交叉验证:

  • tests/src/unit-inspection.cpp 系统地对每一种类型值调用 is_binary():前八个非 binary 类型断言 !j.is_binary(),binary 值断言 j.is_binary(),其覆盖矩阵与官方示例完全一致;
  • tests/src/unit-cbor.cpp 在 CBOR 二进制载荷解析的往返测试中多次用 is_binary() 验证解析产物确实是 binary 类型(例如 j.at("foo").is_binary()),证明该函数是二进制格式解析场景下的常用判型手段;
  • tests/src/unit-regression2.cpp 则验证了 binary 相关场景中判型与访问的组合行为(含自定义类型场景下非 binary 值返回 false)。

这些测试印证了文档声明的语义:is_binary() 对 binary 返回 true,对其余一切类型返回 false,且行为可预期、跨场景稳定。

八、版本历史与使用注意事项

  • 引入版本is_binary()version 3.8.0 起提供(见关联 API 文档的 Version history)。如果你在使用更早的 3.x 版本,需要通过升级或在编译期做版本检测来获得此接口。
  • binary 不属于标准 JSON 文本:由于标准 JSON 没有二进制字面量,binary 值主要面向二进制序列化格式(CBOR、MessagePack、UBJSON、BSON、BJData 等)的解析与生成场景。需要把数据写成文本 JSON 时,通常应在序列化前用 is_binary() 加以识别并自行决定转换策略。
  • 在类型排序中的位置:从 include/nlohmann/detail/value_t.hpp 的类型比较实现可以看出,库为 binary 分配了最大的类型序(其内部顺序表注释为 null < boolean < number < object < array < string < binary),这会影响不同类型 JSON 值之间 <== 等比较运算的走向。
  • 判型函数族的一致性is_binary()is_null()is_array() 等同族函数共享同一 m_data.m_type 判定机制,因此对同一值的多次判型结果互斥且稳定,可以放心在 if-else 链或 switch 分支前先做总括判型。

总而言之,is_binary() 是一个零开销、无异常、可常量求值的类型探针:底层只是对 value_t::binary 标记的一次相等比较,却为你在混合解析文本与二进制数据的代码中提供了清晰可靠的类型分派依据,是安全使用 binary_tget_binary() 以及各二进制格式编解码 API 的入口前提。

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

项目优选

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