nlohmann/json(JSON for Modern C++)basic_json::binary:创建带 subType 的二进制数组值
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::binary 的 basic_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;
}
从源码结构看,实现逻辑非常直接:
- 先默认构造一个空的
basic_json(null 值); - 将内部类型标记改写为
value_t::binary; - 将容器拷贝(引用版本)或移动(
&&版本)进m_data.m_value;带 subtype 的重载会额外构造binary_t(init, subtype)以携带子类型信息; - 以返回值传递(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>;
默认配置下 BinaryType 为 std::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_subtype(std::uint64_t)与 m_has_subtype(bool),提供如下接口:
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_cbor、from_msgpack、from_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() 返回 false、subtype() 返回 0xFFFF...(即 uint64_t 的 -1)。
版本历史与相关接口
binary()自 3.8.0 版本引入;- 相关类型
binary_t的 subtype 在 3.10.0 起改用std::uint64_t表示; - 相关接口:binary_t 类型定义、get_binary、is_binary、byte_container_with_subtype 类文档;
- 本文对应官方文档源文件:docs/mkdocs/docs/api/basic_json/binary.md。
适用前提:二进制值仅服务于 CBOR、MessagePack、BSON(及 UBJSON)等二进制格式的序列化/反序列化,不会被标准 JSON 文本的 dump()/parse() 往返;若你的场景不涉及这些格式,无需接触该 API。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00