Glaze
全球最快的 JSON 库之一。Glaze 从对象内存中读取和写入,简化了接口并提供卓越的性能。
支持的格式:
- JSON |
glaze/json.hpp - BEVE (Binary Efficient Versatile Encoding) |
glaze/beve.hpp - CBOR (Concise Binary Object Representation) |
glaze/cbor.hpp - JSONB (SQLite Binary JSON) |
glaze/jsonb.hpp - BSON (MongoDB Binary JSON) |
glaze/bson.hpp - CSV (Comma Separated Value) |
glaze/csv.hpp - MessagePack |
glaze/msgpack.hpp - Stencil/Mustache (string interpolation) |
glaze/stencil/stencil.hpp - TOML 1.1 (Tom's Obvious, Minimal Language) |
glaze/toml.hpp - YAML |
glaze/yaml.hpp - EETF (Erlang External Term Format) |
glaze/eetf.hpp - And Many More Features
[!NOTE]
Glaze 正在获得 HTTP 支持,包括 REST 服务器、客户端、websockets 等。Glaze 的网络部分正在积极开发中,虽然它已可用且需要反馈,但 API 可能会发生变化并持续改进。
支持 MSVC、Clang 和 GCC 的 C++23 编译时反射!
- 无需编写任何元数据或宏即可读写聚合可初始化结构体!
- 参见 Compiler Explorer 上的示例
C++26 P2996 反射支持
Glaze 现在支持 P2996 "Reflection for C++26"。启用后,P2996 将解锁传统编译时反射无法实现的功能。
P2996 反射功能和示例:
- 非聚合类型 — 具有构造函数、虚函数和继承的类可直接使用
- 自动枚举序列化 — 无需
glz::meta,枚举会自动序列化为字符串 - 无限结构体成员 — 没有 128 个成员的上限
- 私有成员访问 — 无论访问说明符如何,均可反射所有成员
- 标准化 — 无需特定于编译器的技巧,基于
std::meta构建
// Classes with constructors — just works with P2996
class User {
public:
std::string name;
int age;
User() = default;
User(std::string n, int a) : name(std::move(n)), age(a) {}
};
User u{"Alice", 30};
auto json = glz::write_json(u).value_or("error");
// {"name":"Alice","age":30}
// Enums serialize as strings — no glz::meta required
enum class Color { Red, Green, Blue };
Color c = Color::Green;
struct reflect_enums_opts : glz::opts {
bool reflect_enums = true;
};
auto color_json = glz::write<reflect_enums_opts{}>(c).value_or("error");
// "Green"
支持的编译器:GCC 16+ (-std=c++26 -freflection) 和 Bloomberg clang-p2996。请参阅 P2996 文档 了解设置和详细信息。
📖 文档
请参阅此 README、Glaze 文档页面 或 docs 文件夹 以获取文档。
亮点
- 针对结构体的纯编译时反射
- 强大的元特化系统,用于自定义名称和行为
- C++26 P2996 反射 支持 — 非聚合体、自动枚举、无限成员
- 符合 JSON RFC 8259 标准,并支持 UTF-8 验证
- 支持标准 C++ 库
- 仅头文件(独立的格式头文件避免编译开销)
- 直接到内存的序列化/反序列化
- 编译时映射,支持常数时间查找和完美哈希
- 强大的包装器,用于修改读写行为(包装器)
- 使用您自己的自定义读写函数(自定义读写)
- 处理未知键 的方式快速且灵活
- 无异常(使用
-fno-exceptions编译)- 如果您希望使用抛出异常的辅助函数以获得更简洁的语法,请参阅 Glaze 异常
- 无需运行时类型信息(使用
-fno-rtti编译) - JSON Schema 生成
- 部分读取 和 部分写入 支持
- 流式 I/O 用于以有界内存读取/写入大文件
- 更多功能!
性能
| 库 | 往返时间 (s) | 写入 (MB/s) | 读取 (MB/s) |
|---|---|---|---|
| Glaze | 1.01 | 1396 | 1200 |
| simdjson (on demand) | N/A | N/A | 1163 |
| yyjson | 1.22 | 1023 | 1106 |
| reflect_cpp | 3.15 | 488 | 365 |
| daw_json_link | 3.29 | 334 | 479 |
| RapidJSON | 3.76 | 289 | 416 |
| json_struct | 5.87 | 178 | 316 |
| Boost.JSON | 5.38 | 198 | 308 |
| nlohmann | 15.44 | 86 | 81 |
性能注意事项:simdjson 和 yyjson 非常优秀,但当数据不在预期序列中或任何键缺失时,它们会出现严重的性能损失(随着文件大小的增加,问题会加剧,因为它们必须重新遍历文档)。
此外,simdjson 和 yyjson 不支持自动处理转义字符串,因此如果此基准测试中当前未转义的字符串包含转义字符,这些转义字符将不会被处理。
ABC Test 展示了当键的顺序不符合预期时,simdjson 的性能表现不佳:
| Library | Read (MB/s) |
|---|---|
| Glaze | 1219 |
| simdjson (on demand) | 89 |
Binary Performance
带标签的二进制规范:BEVE
| Metric | Roundtrip Time (s) | Write (MB/s) | Read (MB/s) |
|---|---|---|---|
| Raw performance | 0.42 | 3235 | 2468 |
| Equivalent JSON data* | 0.42 | 3547 | 2706 |
JSON 大小:670 字节
BEVE 大小:611 字节
*BEVE 的打包效率比 JSON 更高,因此传输相同数据的速度更快。
Examples
[!TIP]
请参阅 example_json 单元测试,了解如何使用 Glaze 的基本示例。请参阅 json_test 了解功能的全面测试。
您的结构体将自动获得反射支持!用户无需提供元数据。
struct my_struct
{
int i = 287;
double d = 3.14;
std::string hello = "Hello World";
std::array<uint64_t, 3> arr = { 1, 2, 3 };
std::map<std::string, int> map{{"one", 1}, {"two", 2}};
};
JSON(美化后)
{
"i": 287,
"d": 3.14,
"hello": "Hello World",
"arr": [
1,
2,
3
],
"map": {
"one": 1,
"two": 2
}
}
写入 JSON
my_struct s{};
std::string buffer = glz::write_json(s).value_or("error");
或
my_struct s{};
std::string buffer{};
auto ec = glz::write_json(s, buffer);
if (ec) {
// handle error
}
读取 JSON
std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3],"map":{"one":1,"two":2}})";
auto s = glz::read_json<my_struct>(buffer);
if (s) // check std::expected
{
s.value(); // s.value() is a my_struct populated from buffer
}
或
std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3],"map":{"one":1,"two":2}})";
my_struct s{};
auto ec = glz::read_json(s, buffer); // populates s from buffer
if (ec) {
// handle error
}
从文件读取/写入
auto ec = glz::read_file_json(obj, "./obj.json", std::string{});
auto ec = glz::write_file_json(obj, "./obj.json", std::string{});
[!IMPORTANT]
文件名(第二个参数)必须以 null 结尾。
写入流
对于向 std::ostream 目标(文件、网络等)进行流式传输且内存受限的情况:
#include "glaze/core/ostream_buffer.hpp"
std::ofstream file("output.json");
glz::basic_ostream_buffer<std::ofstream> buffer(file); // Concrete type for performance
auto ec = glz::write_json(obj, buffer);
缓冲区在序列化过程中增量刷新,从而以固定内存实现任意大小的输出。有关缓冲区类型和流概念的详细信息,请参阅 流式 I/O。
从流中读取
对于来自 std::istream 源(文件、网络等)的流式传输,且内存受限:
#include "glaze/core/istream_buffer.hpp"
std::ifstream file("input.json");
glz::basic_istream_buffer<std::ifstream> buffer(file);
my_struct obj;
auto ec = glz::read_json(obj, buffer);
缓冲区在解析过程中自动重新填充,从而能够以固定内存读取任意大小的输入。请参阅 流式 I/O 以了解 NDJSON 处理及其他流式模式。
编译器/系统支持
- 需要 C++23
- 已针对 64 位和 32 位进行测试
- 支持小端序和大端序系统
Actions 使用 Clang (18+)、MSVC (Visual Studio 2026 MSVC Build Tools 14.50) 和 GCC (13+) 在 apple、windows 和 linux 上进行构建和测试。大端序通过 s390x 上的 QEMU 模拟进行测试。
Glaze 力求与 GCC 和 Clang 的最新三个版本,以及 MSVC 和 Apple Clang (Xcode) 的最新版本保持兼容。并且,我们旨在仅在主要版本发布时弃用旧版本。
MSVC 编译器标志
Glaze 需要一个符合 C++ 标准的预处理器,这要求在使用 MSVC 构建时启用 /Zc:preprocessor 标志。
SIMD 架构检测
Glaze 会自动检测目标架构,并使用编译器预定义宏启用平台特定的 SIMD 优化,涵盖 x86-64 上的 SSE2 到 AVX-512BW、ARM 上的 NEON 以及 WebAssembly 上的 SIMD128。由于这些是由编译器设置的目标架构宏,交叉编译会自动正常工作(例如,x86 主机为 ARM 进行交叉编译时,不会启用 x86 SIMD 路径)。请参阅 优化性能 以获取完整表格。
要报告构建实际编译的内容,请读取 glz::simd_info:
std::string report;
std::ignore = glz::write_json(glz::simd_info, report);
// {"detected":"AVX512BW","utf8_validation":"AVX512BW","string_escape":"AVX2","float_write":"SSE4.1"}
要禁用 SIMD 优化:
set(glaze_DISABLE_SIMD_WHEN_SUPPORTED ON)
这将 GLZ_DISABLE_SIMD 设置为 INTERFACE 编译定义,并传播到所有链接 glaze::glaze 的目标。如果没有 CMake,请在包含 Glaze 头文件之前定义 GLZ_DISABLE_SIMD。
禁用强制内联
为了更快的编译速度和更小的二进制文件体积,以牺牲峰值性能为代价,请使用 glaze_DISABLE_ALWAYS_INLINE:
set(glaze_DISABLE_ALWAYS_INLINE ON)
注意: 这会减少编译时间和二进制文件大小,对于尺寸至关重要的嵌入式系统可能非常有用。有关进一步减小二进制文件大小的方法,请参阅下文中的优化级别。
C++26 P2996 反射
Glaze 支持 C++26 P2996 反射 作为结构体反射的替代后端。这用标准化的反射原语取代了传统的 __PRETTY_FUNCTION__ 解析。
set(glaze_ENABLE_REFLECTION26 ON)
需要 GCC 16+ 或 Bloomberg clang-p2996,并带有以下标志:
GCC 16+:
-std=c++26 -freflection
Bloomberg clang-p2996:
-std=c++26 -freflection -fexpansion-statements -stdlib=libc++
其优势包括无限制的 struct 成员(传统反射为 128 个)、更简洁的类型名称,以及对未来 C++ 标准的合规性。详见 P2996 Reflection。
优化级别(嵌入式/尺寸优化)
Glaze 提供优化级别,以控制二进制大小与运行时性能之间的权衡。这对于嵌入式系统非常有用:
auto json = glz::write<glz::opts_size{}>(obj);
auto ec = glz::read<glz::opts_size{}>(obj, buffer);
| 级别 | 预设 | 描述 |
|---|---|---|
normal | (默认) | 最大性能(整数和浮点数的查找表约 278KB) |
size | opts_size | 最小二进制(整数表约 400B,浮点数 std::to_chars,线性搜索) |
有关完整详情,请参阅 优化级别。
如何使用 Glaze
FetchContent
include(FetchContent)
FetchContent_Declare(
glaze
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
GIT_TAG main
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(glaze)
target_link_libraries(${PROJECT_NAME} PRIVATE glaze::glaze)
Conan
- 包含在 Conan Center
find_package(glaze REQUIRED)
target_link_libraries(main PRIVATE glaze::glaze)
build2
- 可在 cppget 上获取
import libs = libglaze%lib{glaze}
Arch Linux
- 官方 Arch 仓库
- AUR git 包:glaze-git
参见此示例仓库了解如何在新项目中使用 Glaze
参见 FAQ 获取常见问题解答
显式元数据
如果你想要专门化你的反射,那么你可以可选地编写以下代码:
此元数据对于非聚合可初始化结构体也是必需的。
template <>
struct glz::meta<my_struct> {
using T = my_struct;
static constexpr auto value = object(
&T::i,
&T::d,
&T::hello,
&T::arr,
&T::map
);
};
本地 Glaze 元数据
Glaze 还支持其关联类中的元数据:
struct my_struct
{
int i = 287;
double d = 3.14;
std::string hello = "Hello World";
std::array<uint64_t, 3> arr = { 1, 2, 3 };
std::map<std::string, int> map{{"one", 1}, {"two", 2}};
struct glaze {
using T = my_struct;
static constexpr auto value = glz::object(
&T::i,
&T::d,
&T::hello,
&T::arr,
&T::map
);
};
};
自定义键名或无名称类型
当你定义 Glaze 元数据时,对象会自动反射你的成员对象指针的非静态名称。但是,如果你想要自定义名称,或者你注册了未为字段提供名称的 lambda 函数或包装器,你可以选择在元数据中添加字段名称。
自定义名称的示例:
template <>
struct glz::meta<my_struct> {
using T = my_struct;
static constexpr auto value = object(
"integer", &T::i,
"double", &T::d,
"string", &T::hello,
"array", &T::arr,
"my map", &T::map
);
};
这些字符串均为可选,如果希望名称得以体现,可以针对各个字段将其移除。
以下情况需要名称:
- static constexpr 成员变量
- Wrappers
- Lambda 函数
使用 modify 扩展纯反射
如果你只需要调整少数几个字段,可以使用 glz::meta<T>::modify 将这些更改叠加在自动反射的成员之上:
struct server_status
{
std::string name;
std::string region;
uint64_t active_sessions{};
std::optional<std::string> maintenance;
double cpu_percent{};
};
template <> struct glz::meta<server_status>
{
static constexpr auto modify = glz::object(
"maintenance_alias", [](auto& self) -> auto& { return self.maintenance; },
"cpuPercent", &server_status::cpu_percent
);
};
序列化
server_status status{
.name = "edge-01",
.region = "us-east",
.active_sessions = 2412,
.maintenance = std::string{"scheduled"},
.cpu_percent = 73.5,
};
产生
{
"name": "edge-01",
"region": "us-east",
"active_sessions": 2412,
"maintenance": "scheduled",
"cpu_percent": 73.5,
"maintenance_alias": "scheduled",
"cpuPercent": 73.5
}
所有未修改的成员(name, region, active_sessions, maintenance, cpu_percent)仍然来自纯反射,因此后续添加或移除成员时仍能自动正常工作。只有 modify 中提供的额外键会被叠加在上方。
Reflection API
Glaze 提供了一个编译时反射 API,可通过 glz::meta 特化进行修改。该反射 API 使用纯反射,除非提供了 glz::meta 特化,在这种情况下,默认行为会被开发者覆盖。
static_assert(glz::reflect<my_struct>::size == 5); // Number of fields
static_assert(glz::reflect<my_struct>::keys[0] == "i"); // Access keys
[!WARNING]
上述描述的
glz::reflect字段已经正式确定,不太可能再发生变化。随着我们继续完善规范,其他字段可能会发生变化。
glz::for_each_field
struct test_type {
int32_t int1{};
int64_t int2{};
};
test_type var{42, 43};
glz::for_each_field(var, [](auto& field) {
field += 1;
});
expect(var.int1 == 43);
expect(var.int2 == 44);
自定义读写
可以通过强大的 to/from 特化方法实现自定义读写,该方法在此处有描述:custom-serialization.md。然而,这仅适用于用户定义的类型。
对于常见用例,或需要为特定成员变量设置特殊读写逻辑的情况,可以使用 glz::custom 来注册读写成员函数、std::functions 或 lambda 函数。
参见示例:
struct custom_encoding
{
uint64_t x{};
std::string y{};
std::array<uint32_t, 3> z{};
void read_x(const std::string& s) {
x = std::stoi(s);
}
uint64_t write_x() {
return x;
}
void read_y(const std::string& s) {
y = "hello" + s;
}
auto& write_z() {
z[0] = 5;
return z;
}
};
template <>
struct glz::meta<custom_encoding>
{
using T = custom_encoding;
static constexpr auto value = object("x", custom<&T::read_x, &T::write_x>, //
"y", custom<&T::read_y, &T::y>, //
"z", custom<&T::z, &T::write_z>);
};
suite custom_encoding_test = [] {
"custom_reading"_test = [] {
custom_encoding obj{};
std::string s = R"({"x":"3","y":"world","z":[1,2,3]})";
expect(!glz::read_json(obj, s));
expect(obj.x == 3);
expect(obj.y == "helloworld");
expect(obj.z == std::array<uint32_t, 3>{1, 2, 3});
};
"custom_writing"_test = [] {
custom_encoding obj{};
std::string s = R"({"x":"3","y":"world","z":[1,2,3]})";
expect(!glz::read_json(obj, s));
std::string out{};
expect(not glz::write_json(obj, out));
expect(out == R"({"x":3,"y":"helloworld","z":[5,2,3]})");
};
};
另一个使用 constexpr lambda 的示例:
struct custom_buffer_input
{
std::string str{};
};
template <>
struct glz::meta<custom_buffer_input>
{
static constexpr auto read_x = [](custom_buffer_input& s, const std::string& input) { s.str = input; };
static constexpr auto write_x = [](auto& s) -> auto& { return s.str; };
static constexpr auto value = glz::object("str", glz::custom<read_x, write_x>);
};
suite custom_lambdas_test = [] {
"custom_buffer_input"_test = [] {
std::string s = R"({"str":"Hello!"})";
custom_buffer_input obj{};
expect(!glz::read_json(obj, s));
expect(obj.str == "Hello!");
s.clear();
expect(!glz::write_json(obj, s));
expect(s == R"({"str":"Hello!"})");
expect(obj.str == "Hello!");
};
};
使用 glz::custom 进行错误处理
开发者可以抛出错误,但对于禁用了异常的构建,或者如果希望将错误处理集成到 Glaze 的 context 中,自定义 lambda 的最后一个参数可以是 glz::context&。这实现了与 Glaze 其余部分良好集成的自定义错误处理。
参见示例:
struct age_custom_error_obj
{
int age{};
};
template <>
struct glz::meta<age_custom_error_obj>
{
using T = age_custom_error_obj;
static constexpr auto read_x = [](T& s, int age, glz::context& ctx) {
if (age < 21) {
ctx.error = glz::error_code::constraint_violated;
ctx.custom_error_message = "age too young";
}
else {
s.age = age;
}
};
static constexpr auto value = object("age", glz::custom<read_x, &T::age>);
};
使用中:
age_custom_error_obj obj{};
std::string s = R"({"age":18})";
auto ec = glz::read_json(obj, s);
auto err_msg = glz::format_error(ec, s);
std::cout << err_msg << '\n';
控制台输出:
1:10: constraint_violated
{"age":18}
^ age too young
对象映射
当使用成员指针(例如 &T::a)时,C++ 类结构必须与 JSON 接口匹配。有时可能需要将布局不同的 C++ 类映射到同一个对象接口。这可以通过注册 lambda 函数而不是成员指针来实现。
template <>
struct glz::meta<Thing> {
static constexpr auto value = object(
"i", [](auto&& self) -> auto& { return self.subclass.i; }
);
};
传递给 lambda 函数的值 self 将是一个 Thing 对象,并且 lambda 函数允许我们使该子类对对象接口不可见。
Lambda 函数默认复制返回值,因此通常需要 auto& 返回类型,以便 glaze 能够写入内存。
请注意,通过指针/引用也可以实现重映射,因为 glaze 在写入/读取时以相同方式处理值、指针和引用。
值类型
类可以按以下方式作为底层值处理:
struct S {
int x{};
};
template <>
struct glz::meta<S> {
static constexpr auto value{ &S::x };
};
或使用 lambda:
template <>
struct glz::meta<S> {
static constexpr auto value = [](auto& self) -> auto& { return self.x; };
};
读取约束
Glaze 提供了一个封装器,以便为结构体成员启用复杂的读取约束:glz::read_constraint。
参见示例:
struct constrained_object
{
int age{};
std::string name{};
};
template <>
struct glz::meta<constrained_object>
{
using T = constrained_object;
static constexpr auto limit_age = [](const T&, int age) {
return (age >= 0 && age <= 120);
};
static constexpr auto limit_name = [](const T&, const std::string& name) {
return name.size() <= 8;
};
static constexpr auto value = object("age", read_constraint<&T::age, limit_age, "Age out of range">, //
"name", read_constraint<&T::name, limit_name, "Name is too long">);
};
对于诸如 {"age": -1, "name": "Victor"} 这样的无效输入,Glaze 将输出以下格式化错误消息:
1:11: constraint_violated
{"age": -1, "name": "Victor"}
^ Age out of range
- 成员函数也可以注册为约束。
- 约束 lambda 的第一个字段是父对象,允许用户编写复杂的约束。
读取/写入私有字段
通过创建一个 glz::meta<T> 并在你的类中添加 friend struct glz::meta<T>; 来序列化和反序列化私有字段。
参见示例:
class private_fields_t
{
private:
double cash = 22.0;
std::string currency = "$";
friend struct glz::meta<private_fields_t>;
};
template <>
struct glz::meta<private_fields_t>
{
using T = private_fields_t;
static constexpr auto value = object(&T::cash, &T::currency);
};
suite private_fields_tests = []
{
"private fields"_test = [] {
private_fields_t obj{};
std::string buffer{};
expect(not glz::write_json(obj, buffer));
expect(buffer == R"({"cash":22,"currency":"$"})");
buffer = R"({"cash":2200.0, "currency":"¢"})";
expect(not glz::read_json(obj, buffer));
buffer.clear();
expect(not glz::write_json(obj, buffer));
expect(buffer == R"({"cash":2200,"currency":"¢"})");
};
};
错误处理
Glaze 可安全地用于处理不可信的消息。错误以错误代码的形式返回,通常位于 glz::expected 中,其行为与 std::expected 完全相同。
Glaze 致力于短路错误处理,这意味着如果遇到错误,解析会非常迅速地退出。
要生成更有帮助的错误消息,请调用 format_error:
auto pe = glz::read_json(obj, buffer);
if (pe) {
std::string descriptive_error = glz::format_error(pe, buffer);
}
此测试用例:
{"Hello":"World"x, "color": "red"}
产生此错误:
1:17: expected_comma
{"Hello":"World"x, "color": "red"}
^
表示 x 在此处无效。
读取时消耗的字节数
读取操作返回的 error_ctx 类型包含一个 count 字段,用于指示输入缓冲区中的字节位置:
std::string buffer = R"({"x":1,"y":2})";
my_struct obj{};
auto ec = glz::read_json(obj, buffer);
if (!ec) {
// Success: ec.count contains bytes consumed
size_t bytes_consumed = ec.count;
// bytes_consumed == 13 (entire JSON object)
}
这对于以下场景非常有用:
- 流式处理:从单个缓冲区读取多个 JSON 值
- 部分解析:使用
partial_read选项了解解析停止的位置 - 错误诊断:失败时,
count指示错误发生的位置
输入缓冲区 (Null) 终止
建议输入缓冲区使用非 const std::string,因为这允许 Glaze 通过临时填充来提高性能,并且缓冲区将以 null 结尾。
JSON
默认情况下,选项 null_terminated 被设置为 true,解析 JSON 时必须使用以 null 结尾的缓冲区。可以通过关闭该选项来允许使用非 null 结尾的缓冲区,但会略微降低性能:
constexpr glz::opts options{.null_terminated = false};
auto ec = glz::read<options>(value, buffer); // read in a non-null terminated buffer
BEVE
BEVE 不需要空终止符。这对性能没有影响。
CBOR
CBOR 不需要空终止符。这对性能没有影响。
CSV
CSV 不需要空终止符。这对性能没有影响。
类型支持
数组类型
数组类型在逻辑上转换为 JSON 数组值。使用概念(Concepts)来允许各种容器,甚至如果它们匹配标准库接口,也允许用户自定义容器。
glz::array(编译时混合类型)std::tuple(编译时混合类型)std::arraystd::vectorstd::dequestd::liststd::forward_liststd::spanstd::setstd::unordered_set
对象类型
对象类型在逻辑上转换为 JSON 对象值,例如映射(maps)。与 JSON 一样,Glaze 将对象定义视为无序映射。因此,对象布局的顺序不必与 C++ 中的相同二进制序列匹配。
glz::object(编译时混合类型)std::mapstd::unordered_mapstd::pair(在栈存储中启用动态键)
std::pair被处理为具有单个键和值的对象,但当在数组中使用std::pair时,Glaze 会将这些键值对连接成一个单一的对象。std::vector<std::pair<...>>将序列化为单个对象。如果你不想要这种行为,请设置编译时选项.concatenate = false。
变体
std::variant
有关更多信息,请参阅 Variant Handling。
可空类型
std::unique_ptrstd::shared_ptrstd::optional
可空类型可由有效输入分配,或由 null 关键字置空。
std::unique_ptr<int> ptr{};
std::string buffer{};
expect(not glz::write_json(ptr, buffer));
expect(buffer == "null");
expect(not glz::read_json(ptr, "5"));
expect(*ptr == 5);
buffer.clear();
expect(not glz::write_json(ptr, buffer));
expect(buffer == "5");
expect(not glz::read_json(ptr, "null"));
expect(!bool(ptr));
枚举
默认情况下,枚举将以整数形式写入和读取。如果这是期望的行为,则无需 glz::meta。
但是,如果你更喜欢在 JSON 中以字符串形式使用枚举,可以按照以下方式在 glz::meta 中注册:
enum class Color { Red, Green, Blue };
template <>
struct glz::meta<Color> {
using enum Color;
static constexpr auto value = enumerate(Red,
Green,
Blue
);
};
使用中:
Color color = Color::Red;
std::string buffer{};
glz::write_json(color, buffer);
expect(buffer == "\"Red\"");
[!TIP]
若要实现无需为每个枚举编写元数据的自动枚举到字符串序列化,请使用枚举反射库(magic_enum、enchantum 或 simple_enum),并配合通用的
glz::meta特化。详情请参阅 Automatic Enum Strings。
JSON With Comments (JSONC)
支持在此处定义的规范中使用的注释:JSONC
对注释的读取支持通过 glz::read_jsonc 或 glz::read<glz::opts{.comments = true}>(...) 提供。
Prettify JSON
格式化后的 JSON 可以直接通过编译时选项写出:
auto ec = glz::write<glz::opts{.prettify = true}>(obj, buffer);
或者,JSON 文本可以使用 glz::prettify_json 函数进行格式化:
std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3]})");
auto beautiful = glz::prettify_json(buffer);
beautiful 现在是:
{
"i": 287,
"d": 3.14,
"hello": "Hello World",
"arr": [
1,
2,
3
]
}
压缩 JSON
要写入压缩后的 JSON:
auto ec = glz::write_json(obj, buffer); // default is minified
要压缩 JSON 文本,请调用:
std::string minified = glz::minify_json(buffer);
读取压缩后的 JSON
如果你需要压缩后的 JSON,或者知道你的输入始终为压缩格式,那么你可以使用编译时选项 .minified = true 来获得一点性能提升。
auto ec = glz::read<glz::opts{.minified = true}>(obj, buffer);
布尔标志
Glaze 支持注册一组布尔标志,其行为类似于字符串选项数组:
struct flags_t {
bool x{ true };
bool y{};
bool z{ true };
};
template <>
struct glz::meta<flags_t> {
using T = flags_t;
static constexpr auto value = flags("x", &T::x, "y", &T::y, "z", &T::z);
};
示例:
flags_t s{};
expect(glz::write_json(s) == R"(["x","z"])");
只有 "x" 和 "z" 会被写出,因为它们是 true。从缓冲区读取会设置相应的布尔值。
在写入 BEVE 时,
flags每个布尔值仅使用一个位(按字节对齐)。
记录 JSON
有时你只想尽可能高效地即时写出 JSON 结构。Glaze 提供了类似元组的结构,允许你使用栈分配结构以高速写出 JSON。这些结构对于对象命名为 glz::obj,对于数组命名为 glz::arr。
下面是一个构建包含数组的对象并将其写出的示例。
auto obj = glz::obj{"pi", 3.14, "happy", true, "name", "Stephen", "arr", glz::arr{"Hello", "World", 2}};
std::string s{};
expect(not glz::write_json(obj, s));
expect(s == R"({"pi":3.14,"happy":true,"name":"Stephen","arr":["Hello","World",2]})");
对于通用 JSON,这种方法比
glz::generic显著更快。但可能并不适用于所有场景。
Merge
glz::merge 允许用户将多个 JSON 对象类型合并为单个对象。
glz::obj o{"pi", 3.141};
std::map<std::string_view, int> map = {{"a", 1}, {"b", 2}, {"c", 3}};
auto merged = glz::merge{o, map};
std::string s{};
glz::write_json(merged, s); // will write out a single, merged object
// s is now: {"pi":3.141,"a":0,"b":2,"c":3}
glz::merge存储 lvalue 的引用以避免拷贝
通用 JSON
参见 Generic JSON 以了解 glz::generic。
glz::generic json{};
std::string buffer = R"([5,"Hello World",{"pi":3.14}])";
glz::read_json(json, buffer);
assert(json[2]["pi"].get<double>() == 3.14);
惰性 JSON
参见 Lazy JSON 用于 glz::lazy_json。
std::string json = R"({"name":"John","age":30,"city":"New York"})";
auto result = glz::lazy_json(json);
if (result) {
auto age = (*result)["age"].get<int>(); // Only parses what you access
}
glz::lazy_json提供按需解析功能,无需任何预处理,非常适合从大型 JSON 文档中提取少量字段。
glz::lazy_beve 为 BEVE 二进制格式提供相同的惰性解析能力。参见 Lazy BEVE。
Raw Buffer Performance
Glaze 写入 std::string 的速度几乎与写入原始 char 缓冲区一样快。如果缓冲区中有足够分配的可用空间,你可以像下面所示那样写入原始缓冲区,但不建议这样做。
glz::read_json(obj, buffer);
const auto result = glz::write_json(obj, buffer.data());
if (!result) {
buffer.resize(result.count);
}
写入固定大小的缓冲区
所有写入函数均返回 glz::error_ctx,该值同时提供错误信息和字节数:
std::array<char, 1024> buffer;
auto ec = glz::write_json(my_obj, buffer);
if (ec) {
if (ec.ec == glz::error_code::buffer_overflow) {
// Buffer was too small
std::cerr << "Overflow after " << ec.count << " bytes\n";
}
return;
}
// Success: ec.count contains bytes written
std::string_view json(buffer.data(), ec.count);
error_ctx 类型提供:
if (ec)- 当存在错误时为 true(符合std::error_code语义)ec.count- 已处理的字节数(始终填充,即使在错误时)ec.ec- 错误代码glz::format_error(ec, buffer)- 格式化的错误消息
编译时选项
glz::opts 结构体定义了用于读取/写入的默认编译时选项。
除了调用 glz::read_json(...),你还可以调用 glz::read<glz::opts{}>(...) 并自定义选项。
例如:glz::read<glz::opts{.error_on_unknown_keys = false}>(...) 将关闭对未知键的错误处理,并简单跳过这些项。
glz::opts 还可以在不同格式之间切换:
glz::read<glz::opts{.format = glz::BEVE}>(...)->glz::read_beve(...)glz::read<glz::opts{.format = glz::JSON}>(...)->glz::read_json(...)
[!IMPORTANT]
请参阅 Options 以获取所有编译时选项的综合参考表,包括可添加到自定义选项结构体中的可继承选项。
常用编译时选项
glz::opts 结构体提供默认选项。以下是最常用的选项:
| 选项 | 默认值 | 描述 |
|---|---|---|
format | JSON | 格式选择器(JSON、BEVE、CSV、TOML) |
null_terminated | true | 输入缓冲区是否以空字符结尾 |
error_on_unknown_keys | true | 遇到未知 JSON 键时是否报错 |
skip_null_members | true | 写入时是否跳过 null 值 |
prettify | false | 是否输出格式化 JSON |
minified | false | 是否要求输入为压缩格式(解析更快) |
error_on_missing_keys | false | 是否要求所有键都存在 |
partial_read | false | 读取完最深层对象后是否退出 |
可继承选项(默认不在 glz::opts 中)可通过自定义结构体添加:
struct my_opts : glz::opts {
bool validate_skipped = true; // Full validation on skipped values
bool append_arrays = true; // Append to arrays instead of replace
};
constexpr my_opts opts{};
auto ec = glz::read<opts>(obj, buffer);
JSON 一致性
默认情况下,Glaze 严格符合最新的 JSON 标准,除了两个具有相关选项的情况:
validate_skipped此选项在解析时对跳过的值执行完整的 JSON 验证。默认未设置此选项,因为当用户不关心某些值时,通常会跳过这些值,并且 Glaze 仍然会验证主要问题。但是,通过不关心跳过的值是否完全符合 JSON 规范,这使得跳过操作更快。例如,默认情况下,Glaze 会确保跳过的数字包含所有有效的数字字符,但除非开启validate_skipped,否则它不会验证诸如跳过数字中的前导零等问题。在 Glaze 解析用于使用的值时,会进行完整验证。validate_trailing_whitespace此选项验证已解析文档中的尾部空白。由于 Glaze 解析 C++ 结构体,通常不需要在读取完感兴趣的对象后继续解析。如果您希望确保文档的其余部分具有有效的空白,请开启此选项,否则 Glaze 将在解析完感兴趣的内容后忽略后续内容。
读取时会验证 UTF-8 编码,因为 RFC 8259 第 8.1 节要求如此。validate_utf8 选项会为此关闭验证,适用于编码已得到保证的输入,代价是失去一致性。参见 UTF-8 Validation。
[!NOTE]
默认情况下,Glaze 不会对控制字符进行 Unicode 转义(例如
"\x1f"到"\u001f"),因为这存在在字符串中嵌入空字符和其他不可见字符的风险。对于希望在字符串中将控制字符写为转义 Unicode 的用户,可以使用编译时选项escape_control_characters。// Example options for enabling escape_control_characters struct options : glz::opts { bool escape_control_characters = true; };
Skip
在对象中确认某个键的存在以防止错误可能很有用,但该值在 C++ 中可能不需要或不存在。这些情况通过向元数据注册 glz::skip 类型来处理。
参见示例:
struct S {
int i{};
};
template <>
struct glz::meta<S> {
static constexpr auto value = object("key_to_skip", skip{}, &S::i);
};
std::string buffer = R"({"key_to_skip": [1,2,3], "i": 7})";
S s{};
glz::read_json(s, buffer);
// The value [1,2,3] will be skipped
expect(s.i == 7); // only the value i will be read into
Hide
Glaze 旨在帮助构建通用 API。有时需要将某个值暴露给 API,但不希望在 JSON 中读取或写入该值。这正是 glz::hide 的使用场景。
glz::hide 会将该值从 JSON 输出中隐藏,同时仍允许通过 API(以及 JSON 指针)访问。
参见示例:
struct hide_struct {
int i = 287;
double d = 3.14;
std::string hello = "Hello World";
};
template <>
struct glz::meta<hide_struct> {
using T = hide_struct;
static constexpr auto value = object(&T::i, //
&T::d, //
"hello", hide{&T::hello});
};
hide_struct s{};
auto b = glz::write_json(s);
expect(b == R"({"i":287,"d":3.14})"); // notice that "hello" is hidden from the output
带引号的数字
你可以利用 glz::quoted 包装器,将带引号的 JSON 数字直接解析为 double、int 等类型。
struct A {
double x;
std::vector<uint32_t> y;
};
template <>
struct glz::meta<A> {
static constexpr auto value = object("x", glz::quoted_num<&A::x>, "y", glz::quoted_num<&A::y>);
};
{
"x": "3.14",
"y": ["1", "2", "3"]
}
带引号的 JSON 数字将被直接解析为 double 和 std::vector<uint32_t>。glz::quoted 函数同样适用于嵌套的对象和数组。
JSON Lines (NDJSON) 支持
Glaze 支持 JSON Lines(或 Newline Delimited JSON)用于类数组类型(例如 std::vector 和 std::tuple)。
std::vector<std::string> x = { "Hello", "World", "Ice", "Cream" };
std::string s = glz::write_ndjson(x).value_or("error");
auto ec = glz::read_ndjson(x, s);
更多功能
数据记录器
命令行界面菜单
JMESPath
- 查询 JSON
JSON 包含系统
JSON Pointer 语法
JSON-RPC 2.0
JSON Schema
共享库 API
流式 I/O
带标签的二进制消息
线程池
时间跟踪性能分析
- 将性能配置文件输出为 JSON 并使用 Perfetto 进行可视化
封装器
扩展
请参阅 ext 目录以获取扩展。
许可证
Glaze 根据 MIT 许可证分发,但嵌入式形式除外:
--- 许可证的可选例外 ---
作为例外,如果由于您编译源代码的结果,本软件的部分内容被嵌入到该源代码的机器可执行对象形式中,您可以重新分发此类嵌入式部分,而无需包含版权声明和许可声明。