首页
/ nlohmann/json(JSON for Modern C++)basic_json::binary:创建带 subType 的二进制数组值

nlohmann/json(JSON for Modern C++)basic_json::binary:创建带 subType 的二进制数组值

2026-09-05 14:22:37作者:龚格成

basic_json::binary 是 nlohmann/json(JSON for Modern C++)库中专门用于显式创建 JSON 二进制数组值(binary value)的静态工厂函数。二进制值并非标准 JSON 的一部分,而是为了兼容 CBOR、MessagePack、BSON 等二进制序列化格式而引入的非标准扩展。阅读本文后,你将掌握 binary() 全部四个重载的签名、参数语义与异常保证,理解它为什么必须是一个独立的静态函数而不能通过普通构造函数隐式产生,并能结合仓库源码看清其底层存储结构与 subtype 在三种二进制格式中的序列化差异。

函数签名与参数说明

basic_json::binary 提供四组重载,均按值语义返回一个新的 basic_json 对象:

// (1) 无 subtype
static basic_json binary(const typename binary_t::container_type& init);
static basic_json binary(typename binary_t::container_type&& init);

// (2) 带 subtype
static basic_json binary(const typename binary_t::container_type& init,
                         std::uint8_t subtype);
static basic_json binary(typename binary_t::container_type&& init,
                         std::uint8_t subtype);
  • 重载 (1):从给定的二进制容器创建一个 JSON 二进制数组值;
  • 重载 (2):从给定的二进制容器并附带 subtype 创建一个 JSON 二进制数组值。

参数说明如下:

参数 方向 含义
init in 包含要用作二进制类型字节的容器(默认即 std::vector<std::uint8_t>
subtype in 在 CBOR、MessagePack、BSON 中使用的子类型标识

返回值是一个 JSON 二进制数组值(即 type()value_t::binarybasic_json 实例)。

异常安全性:提供强保证(Strong guarantee)——若抛出异常,JSON 值不发生任何改变。

复杂度:随 init 的大小线性增长;typename binary_t::container_type&& init(右值引用)版本为常数时间,因为底层直接执行移动语义。

为什么需要独立的静态工厂函数

这是理解 binary() 设计动机的关键。JSON 数组(array)和 JSON 二进制数组(binary array)在底层都以某种 std::vector 形式承载,两者在模板层面高度相似,导致在标准的 value 构造函数中很难正确区分并写出对应的模板重载。由于 JSON 二进制数组属于非标准扩展,库的作者决定禁止通过常规构造路径自动初始化出二进制数组类型,原因有二:一是向后兼容——默认的 nlohmann::json 至今仍将 std::vector<std::uint8_t> 视为数组而非二进制值;二是避免用户误打误撞产生一个二进制值。因此库选择提供显式的静态工厂 binary(),把"我要创建二进制值"的意图写死在调用点上。

这一点也可从仓库文档 binary_t 说明 中得到印证:默认配置下直接给 json 赋值一个 std::vector<std::uint8_t> 会得到 array 类型;只有当用户配置了非默认的自定义 BinaryType(如 std::vector<std::byte>)时,该容器类型才会被自动识别为二进制值。

源码实现剖析

四个重载的实现在单头文件 single_include/nlohmann/json.hpp 中(模块化头文件 include/nlohmann/json.hpp 中实现完全一致)。以带 subtype 的引用版本为例:

JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json binary(const typename binary_t::container_type& init,
                         typename binary_t::subtype_type subtype)
{
    auto res = basic_json();
    res.m_data.m_type = value_t::binary;
    res.m_data.m_value = binary_t(init, subtype);
    return res;
}

从源码结构看,实现逻辑非常直接:

  1. 先默认构造一个空的 basic_json(null 值);
  2. 将内部类型标记改写为 value_t::binary
  3. 将容器拷贝(引用版本)或移动(&& 版本)进 m_data.m_value;带 subtype 的重载会额外构造 binary_t(init, subtype) 以携带子类型信息;
  4. 以返回值传递(RVO/NRVO)方式返回结果。

JSON_HEDLEY_WARN_UNUSED_RESULT 宏则提示开发者:该函数的返回值若被丢弃会产生编译告警——创建一个二进制值却不使用它通常意味着调用失误。

底层存储类型 binary_t

binary() 的参数类型来自 binary_t,其定义见 include/nlohmann/json.hpp

using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;

默认配置下 BinaryTypestd::vector<std::uint8_t>,因此 binary_t 默认即 byte_container_with_subtype<std::vector<std::uint8_t>>。该类型由 include/nlohmann/byte_container_with_subtype.hpp 中的 byte_container_with_subtype 模板类实现:它直接继承自 BinaryType(即"继承即容器"),并额外维护两个私有成员 m_subtypestd::uint64_t)与 m_has_subtypebool),提供如下接口:

  • set_subtype(subtype_type):设置子类型;
  • subtype():返回子类型;未设置时返回 static_cast<subtype_type>(-1)
  • has_subtype():是否已设置子类型;
  • clear_subtype():清空子类型。

需要注意的是 subtype 的承载类型为 std::uint64_t(在 3.10.0 版本中从 8 位类型升级而来),这足以容纳 CBOR 与 BSON 两种使用二进制子类型的格式各自的编号空间;不过两种格式的具体编号互不兼容,相互转换需由使用者自行完成。

subtype 在 CBOR / MessagePack / BSON 中的序列化行为

binary() 创建的值的用途是序列化到二进制格式,subtype 的落地方式因格式而异(依据 binary_t 文档 的 Notes 部分):

  • CBOR:二进制值表示为 byte string,subtype 以 tag 的形式写出;
  • MessagePack
    • 若提供了 subtype,且字节数恰好为 1、2、4、8 或 16,使用 fixext 家族(fixext1/2/4/8/16);其他长度使用 ext 家族(ext8/ext16/ext32)。subtype 以有符号 8 位整数附加;
    • 若未提供 subtype,则使用 bin 家族(bin8/bin16/bin32);
  • BSON:若提供 subtype 则以无符号 8 位整数写出;未提供时回退为通用二进制子类型 0x00(BSON 规范中被称为 "generic binary subtype",是驱动与工具的默认选择)。

创建二进制值之后,典型的使用路径是:用 get_binary 取得 binary_t& 引用来读写容器与 subtype,或用 is_binary 判断值类型;库同时提供 from_cborfrom_msgpackfrom_bson 等静态方法完成解析,形成"解析 → 内存中 binary 值 → 再序列化"的往返链路。

完整示例

下面示例与仓库文档配套的可运行示例 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;
}

编译运行输出(见 binary.output):

type: binary, subtype: 42

示例演示了两件事:其一,json::binary(vec, 42) 显式产生 binary 类型的值(type_name() 输出 binary);其二,get_binary().subtype() 能原样读回 42,说明 subtype 已随值保存在 binary_t 中。若改用无 subtype 的重载 json::binary(vec),则 has_subtype() 返回 falsesubtype() 返回 0xFFFF...(即 uint64_t 的 -1)。

版本历史与相关接口

适用前提:二进制值仅服务于 CBOR、MessagePack、BSON(及 UBJSON)等二进制格式的序列化/反序列化,不会被标准 JSON 文本的 dump()/parse() 往返;若你的场景不涉及这些格式,无需接触该 API。

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