diff --git a/doc/library-detail_zh_Hans.adoc b/doc/library-detail_zh_Hans.adoc new file mode 100644 index 0000000..af49c97 --- /dev/null +++ b/doc/library-detail_zh_Hans.adoc @@ -0,0 +1,122 @@ +https://www.boost.org/doc/libs/release/libs/json/[image:https://raw.githubusercontent.com/CPPAlliance/json/master/doc/images/repo-logo-3.png[Boost.JSON]] + +[width="100%", cols="12%,43%,45%", options="header"] +|=== +|分支 |https://github.com/boostorg/json/tree/master[`master`] +|https://github.com/boostorg/json/tree/develop[`develop`] +|https://azure.microsoft.com/en-us/services/devops/pipelines/[Azure] +|https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=5&branchName=master[image:https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/master[构建状态]] +|https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=8&branchName=develop[image:https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/develop[构建状态]] + +|文档 +|https://www.boost.org/doc/libs/master/libs/json/[image:https://img.shields.io/badge/docs-master-brightgreen.svg[文档]] +|https://www.boost.org/doc/libs/develop/libs/json/[image:https://img.shields.io/badge/docs-develop-brightgreen.svg[文档]] + +|https://drone.io/[Drone] +|https://drone.cpp.al/boostorg/json[image:https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/master[构建状态]] +|https://drone.cpp.al/boostorg/json[image:https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/develop[构建状态]] + +|测试矩阵 +|http://www.boost.org/development/tests/master/developer/json.html[image:https://img.shields.io/badge/matrix-master-brightgreen.svg[测试矩阵]] +|http://www.boost.org/development/tests/develop/developer/json.html[image:https://img.shields.io/badge/matrix-develop-brightgreen.svg[测试矩阵]] + +|模糊测试 |— +|https://github.com/boostorg/json/actions?query=workflow%3Afuzz+branch%3Adevelop[image:https://github.com/boostorg/json/workflows/fuzz/badge.svg?branch=develop[模糊测试]] + +|https://ci.appveyor.com/[Appveyor] +|https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/master[image:https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=master&svg=true[构建状态]] +|https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/develop[image:https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=develop&svg=true[构建状态]] + +|https://codecov.io[codecov.io] +|https://codecov.io/gh/boostorg/json/branch/master[image:https://codecov.io/gh/boostorg/json/branch/master/graph/badge.svg[codecov]] +|https://codecov.io/gh/boostorg/json/branch/develop[image:https://codecov.io/gh/boostorg/json/branch/develop/graph/badge.svg[codecov]] +|=== + +== Boost.JSON + +=== 概述 + +Boost.JSON 是一个可移植的 C++ 库,提供了实现 [JavaScript Object Notation](https://json.org/)(简称 “JSON”)的容器和算法,这是一种轻量级的数据交换格式。该格式易于人类读写,也易于机器解析和生成。该格式基于 JavaScript 编程语言的一个子集([ECMA-262 标准](https://www.ecma-international.org/ecma-262/10.0/index.html)),目前已在 [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) 中标准化。JSON 是一种独立于语言但使用 C 语言家族(包括 C、C++、C#、Java、JavaScript、Perl、Python 等)程序员熟悉的约定的文本格式。这些特性使 JSON 成为理想的数据交换语言。 + +该库专注于一个常见且广泛使用的场景:解析和序列化到一个名为 `value` 的容器,或从中进行解析和序列化(该容器用于存储 JSON 类型)。任何构建的 `value` 都可以被序列化后再反序列化,并保证结果与原始值相等。使用该库生成的任何 JSON 输出,均可被大多数主流编程语言中的常见 JSON 实现正确读取。 + +`value` 容器被设计为一种适合作为公共接口和库中词汇类型的组件,便于组合使用。该库将可表示的数据类型限制在绝大多数 JSON 实现(尤其是 JavaScript)普遍接受的范围内。解析器和序列化器均具备高性能,其基准性能达到或超过了同类优秀库的水平。对分配器的支持非常完善。使用这些类型的代码将易于理解、灵活且高效。 + +Boost.JSON 提供以下特性: + +* 快速编译 +* 仅需 C++11 +* 高性能流式解析器和序列化器 +* 对象的常量时间键查找 +* 允许非标准 JSON 的选项 +* 提供易于使用且安全的现代 API,并支持分配器 +* 可选的纯头文件方式,无需链接库 + +若要获取完整文档请访问以下链接: https://boost.org/libs/json + +=== 要求 + +* 仅需 C++11 +* 可链接预构建的静态或动态 Boost 库,也可使用纯头文件方式(见下文)。 +* Supports -fno-exceptions(可自动检测) + +该库在其接口中大量依赖以下众所周知的 C++ 类型(以下称为 _standard types_): + +* `string_view` +* `memory_resource`, `polymorphic_allocator` +* `error_category`, `error_code`, `error_condition`, `system_error` + +==== 纯头文件 + +要使用纯头文件模式(即无需将程序链接到静态或动态的 Boost.JSON 库),只需在项目中恰好一个新建或现有源文件中添加以下行。 + +.... +#include +.... + +MSVC 用户还必须定义宏 `BOOST_JSON_NO_LIB` ,以禁用自动链接。 + +==== 嵌入式 + +Boost.JSON 在嵌入式设备上表现优异。该库使用局部栈缓冲区来提升某些操作的性能。在 Intel 平台上,这些缓冲区较大(4KB),而在非 Intel 平台上则较小(256 字节)。若要在嵌入式应用中调整栈缓冲区的大小,请在构建库或包含函数定义时定义以下宏: + +.... +#define BOOST_JSON_STACK_BUFFER_SIZE 1024 +#include +.... + +==== 支持的编译器 + +Boost.JSON 已在以下编译器上进行过测试: + +* clang: 3.5, 3.6, 3.7, 3.8, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +* gcc: 4.8, 4.9, 5, 6, 7, 8, 9, 10, 11, 12 +* msvc: 14.0, 14.1, 14.2, 14.3 + +==== 支持的 JSON 文本 + +该库要求输入文本使用 UTF-8 编码,这是 [RFC](https://datatracker.ietf.org/doc/html/rfc8259#section-8.1) 对系统间交换的所有 JSON 所规定的强制要求。同样,该库生成的文本也是有效的 UTF-8。 + +RFC 不允许字节顺序标记(BOM)出现在 JSON 文本中,因此该库将 BOM 视为语法错误。 + +该库支持多种流行的 JSON 扩展,但这些扩展必须显式启用。 + +==== Visual Studio 解决方案 + +.... +cmake -G "Visual Studio 16 2019" -A Win32 -B bin -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake +cmake -G "Visual Studio 16 2019" -A x64 -B bin64 -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake +.... + +=== 质量保证 + +该库的开发基础设施包括以下每次提交的分析: + +* 覆盖率报告 +* 基准性能比较 +* 在 Drone.io、Azure Pipelines 和 AppVeyor 上进行编译与测试 +* 使用 clang-llvm 和机器学习的模糊测试 + +=== 许可证 + +根据 Boost 软件许可协议 1.0 版分发。(详见随附文件 LICENSE_1_0.txt 文件或访问 https://www.boost.org/LICENSE_1_0.txt ) diff --git a/doc/pages/allocators/background_zh_Hans.adoc b/doc/pages/allocators/background_zh_Hans.adoc new file mode 100644 index 0000000..0a6fe08 --- /dev/null +++ b/doc/pages/allocators/background_zh_Hans.adoc @@ -0,0 +1,89 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 背景 +C++中分配器的第一个版本定义了名为{req_Allocator}的要求,并将每个标准容器设计为以分配器类型为模板参数的类模板。例如,以下是 {std_vector} 的声明: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_1,indent=0] +---- + +标准分配器是{req_DefaultConstructible}。为支持有状态分配器,容器提供了额外的构造函数重载,接受一个分配器实例参数。 + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_2,indent=0] +---- + +尽管该机制能够正常工作,但仍存在一些可用性问题: + +* 容器必须是一个类模板。 +* 在元素类型上参数化分配器的方式显得笨拙。 +* 分配器特征机制(尤其是 POCCA 和 POCMA)复杂且容易出错。 + +使用多种分配器类型的基于分配器的程序会引发更多的函数模板实例化,且通常编译速度更慢,因为类模板的函数定义必须在所有调用点可见。 + +== 多态分配器 + +{cpp}17通过引入一个名为 {ref_memory_resource} 的抽象接口来表示底层分配操作,从而改进分配器模型。该接口未在元素类型上参数化,且无特征: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_3,indent=0] +---- + +类模板{ref_polymorphic_allocator}包装了{ref_memory_resource}指针,并满足{req_Allocator}要求,使其可在需要分配器的地方使用。标准库为使用多态分配器的标准容器提供了类型别名: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_4,indent=0] +---- + +多态分配器通过一个指向内存资源的指针进行构造: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_5,indent=0] +---- + +内存资源通过指针传递;所有权不发生转移。调用方需负责确保该内存资源的生命周期持续到所有使用它的容器都离开作用域为止,否则行为未定义。在某些场景下,这种模型是合适的,例如以下示例中使用了一个基于局部栈缓冲区构造的单调内存资源: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_6,indent=0] +---- + +然而,有时需要共享所有权,即内存资源的生命周期应自动延长。例如,若某个库希望返回一个容器,该容器拥有该库自定义内存资源的实例,如下所示: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_7,indent=0] +---- + +可通过声明容器使用自定义分配器(可能使用`std::shared_ptr< std::pmr::memory_resource >`作为数据成员)来解决此问题。这会阻碍库的组合;每个库都会导出各自独特且互不兼容的容器类型。原始内存资源指针也容易被误用: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_8,indent=0] +---- + +针对此问题的变通方案往往比问题本身更糟糕。该库可以返回一个包含 vector 和`std::unique_ptr`的pair,必须由调用方自行管理。或者,该库也可以修改其函数签名,接受调用方提供的{ref_memory_resource}``*``,同时将所需的内存资源(如上文的`my_resource`)公开。 + +== 问题陈述 + +我们希望采用一种使用单一类型`T`的分配器模型,该模型具有以下特性: + +* `T`不是类模板 +* `T`引用一个 {ref_memory_resource} +* `T`同时支持引用语义和共享所有权 +* `T`能与已使用`std::pmr`的代码互操作 + +Boost.JSON通过引入一个名为<> 的新型智能指针解决此问题,该指针基于{cpp}17的内存分配接口构建,实现了上述目标。因此,使用该类型的库更易于组合,并能获得更快的编译速度,因为使用该类型的容器的成员函数可以定义在类外(out-of-line)。 diff --git a/doc/pages/allocators/overview_zh_Hans.adoc b/doc/pages/allocators/overview_zh_Hans.adoc new file mode 100644 index 0000000..a5d994a --- /dev/null +++ b/doc/pages/allocators/overview_zh_Hans.adoc @@ -0,0 +1,22 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 分配器 + +本文将首先讨论C++标准中使用的各种分配器模型,随后介绍本库所采用的模型及其优势,最后说明该库如何与使用多态分配器的现有代码进行互操作。 + +:leveloffset: +1 + +include::background.adoc[] +include::storage_ptr.adoc[] +include::uses_allocator.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/allocators/storage_ptr_zh_Hans.adoc b/doc/pages/allocators/storage_ptr_zh_Hans.adoc new file mode 100644 index 0000000..cbaecd0 --- /dev/null +++ b/doc/pages/allocators/storage_ptr_zh_Hans.adoc @@ -0,0 +1,149 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += `storage_ptr` +本库中的可变长度容器均使用动态分配的内存来存储其内容。调用方可通过在特定构造函数和函数参数列表中指定 <> ,以控制所使用的内存分配策略。<> 具有以下特性: + +* 存储指针始终指向一个有效的、类型擦除后的 {ref_memory_resource}。 + +* 默认构造的存储指针引用 <>(默认资源),这是一个由实现定义的实例,始终使用等效于全局 operator new 和 operator delete 的方式。 + +* 从 {ref_memory_resource} 或 {ref_polymorphic_allocator} 构造的存储指针不获取所有权;调用方负责确保资源的生命周期持续到不再被引用为止。 + +* 通过 <> 获取的存储指针会获得对内存资源的共享所有权;该资源的生命周期将延长,直至所有该存储指针的副本均被销毁。 + +* 存储指针在对资源进行类型擦除之前会记录 <> 的值,从而允许在运行时查询该值。 + +以下是使用该库时所有与分配相关的类型和函数列表: + +.函数与类型 +|=== +|Name|描述 + +| <> +| 返回一个指向内存资源实例的指针,该实例在分配内存时总是抛出异常。此机制用于确保解析或容器操作不会进行动态内存分配,从而维持该不变性。 + +| <> +| 一个自定义点,允许内存资源类型表明其 deallocate 调用是平凡的。 + +| <> +| 一个返回智能指针的函数,该智能指针对新分配的内存资源具有共享所有权。 + +| {ref_memory_resource} +| 表示分配器的抽象基类。 + +| <> +| 一种内存资源,用于分配大块内存,且其 deallocate 函数是平凡的。所分配的内存不会被释放,直到该资源被销毁,因此在解析场景下速度很快,但不适合执行修改操作。 + +| {ref_polymorphic_allocator} +| 一个 {req_Allocator},它通过引用 {ref_memory_resource} 执行内存分配。 + +| <> +| 一种使用调用方提供的单一缓冲区的内存资源,不进行任何动态分配。该资源在解析时速度很快,但不适合执行修改操作。 + +| <> +| 一种用于管理和访问 {ref_memory_resource} 的智能指针。 +|=== + +== 默认内存资源 +默认内存资源使用全局 `operator new` 和 `operator delete` 来进行内存分配。该资源不采用引用计数,且其 deallocate 函数是非平凡的。所有默认构造的 <> 对象均引用同一个内存资源: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_1,indent=0] +---- + +默认构造的库容器使用默认内存资源: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_2,indent=0] +---- + +默认内存资源适用于一般用途。它在解析时提供合理的性能,在修改容器内容时则具有保守的内存使用特性。 + +[NOTE] +该内存资源不保证与 `boost::container::pmr::get_default_resource` 的返回结果相同,也无法通过 `boost::container::pmr::set_default_resource` 进行更改。 + +== 单调资源 +考虑解析过程中的内存分配模式:当遇到数组、对象或字符串时,解析器会将其元素累积在临时存储区中。当所有元素都已知后,在构造值时会向内存资源发起一次内存分配请求。因此,解析过程仅在容器最终大小确定时进行一次分配和构造,不会发生内存重分配;也就是说,内存缓冲区无需通过分配更大的新缓冲区并释放旧缓冲区的方式来扩容。 + +<> 通过在内部分配逐渐增大的全局内存块,并将这些块切分为更小的部分以满足分配请求,从而优化了该内存分配模式。它具有平凡的 deallocate 函数,实际上不会释放内存,直到该资源被销毁。因此,它非常适合用于解析 JSON 后仅检查结果值而不对其进行修改的场景。 + +在调用 <> 时,可按如下方式指定构造值所使用的内存资源: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_3,indent=0] +---- + +或者,可按如下方式解析到一个对内存资源具有共享所有权的值: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_4,indent=0] +---- + +单调资源在构造时可选择性地指定一个初始缓冲区,优先使用该缓冲区,之后才会转而使用堆内存。这使得调用方可以利用栈空间,避免对大多数 JSON 解析进行动态分配;仅当输入的 JSON 大于平均水平时,才会退回到从堆中进行动态分配,如下所示: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_5,indent=0] +---- + +== 静态资源 +<> 由调用方提供的缓冲区构造而成,并从该缓冲区满足所有内存分配请求。一旦缓冲区耗尽,后续的分配调用将抛出 `std::bad_alloc` 异常。该资源提供一个简单的不变式:永不执行动态堆分配。 + +要使用该资源,请用一个局部缓冲区对其进行构造: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_6,indent=0] +---- + +== 空资源 +函数 <> 返回一个全局的空资源实例。该资源提供一个简单的不变式:所有内存分配调用都会抛出 `std::bad_alloc` 异常。通过使用空资源实例,可确保解析过程绝不会进行堆内存分配。这一点将在后文进一步详细说明。 + +== 分配器传播 +容器 <>, <> 和 <> 在构造时所使用的内存资源会自动传播给其子元素: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_7,indent=0] +---- + +这种传播是递归进行的:嵌套容器中的所有子容器都会获得相同的内存资源。一旦容器被构造,其内存资源就永远无法更改。 + +== 资源生命周期 +需要注意的是,<> 同时支持共享所有权和引用生命周期模型。从内存资源指针构造时不会转移所有权: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_8,indent=0] +---- + +以这种方式使用内存资源时(包括从 {ref_polymorphic_allocator} 构造存储指针或容器的情况),调用方必须确保资源的生命周期延长至不再被任何变量引用为止;否则,可能会出现未定义行为。 + +通过函数 <> 可实现共享所有权,该函数会动态分配内存以创建一个新的、带引用计数的内存资源,并将其以 <> 的形式返回: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_9,indent=0] +---- + +当存储指针以这种方式构造时,其所引用的内存资源的生命周期将延长,直至所有引用它的变量均被销毁为止。 + +== 用户定义资源 +要实现自定义内存分配策略,请从 {ref_memory_resource} 派生类,并实现函数 `do_allocate`、`do_deallocate` 和 `do_is_equal`,如下例所示,该示例将其执行的每个操作记录到控制台: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_10,indent=0] +---- diff --git a/doc/pages/allocators/uses_allocator_zh_Hans.adoc b/doc/pages/allocators/uses_allocator_zh_Hans.adoc new file mode 100644 index 0000000..3501b6d --- /dev/null +++ b/doc/pages/allocators/uses_allocator_zh_Hans.adoc @@ -0,0 +1,36 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#uses_allocator] += 使用分配器构造 +为支持已使用多态分配器的代码库,本库中的容器支持 {std_uses_allocator} 构造。对于 <>, <>, <> 和 <>: + +* 嵌套类型 `allocator_type` 是{ref_polymorphic_allocator}的别名。 + +* 所有接受 <> 的合格构造函数,也将在相同参数位置接受一个 {ref_polymorphic_allocator} 实例。 + +* 成员函数 `get_allocator` 返回一个 + 由容器所使用的 {ref_memory_resource} 构造的 {ref_polymorphic_allocator} 实例。该内存资源的所有权不会被转移。 + +实际上,这意味着当库中的容器类型被用在使用多态分配器的标准容器中时,该分配器会传播到 JSON 类型。例如: + +[source] +---- +include::../../../test/doc_uses_allocator.cpp[tag=doc_uses_allocator_1,indent=0] +---- + +库容器可以从多态分配器构造: + +[source] +---- +include::../../../test/doc_uses_allocator.cpp[tag=doc_uses_allocator_2,indent=0] +---- + +多态分配器会递归传播。子元素的子元素将使用与父元素相同的内存资源。 diff --git a/doc/pages/benchmarks_zh_Hans.adoc b/doc/pages/benchmarks_zh_Hans.adoc new file mode 100644 index 0000000..12ff1b8 --- /dev/null +++ b/doc/pages/benchmarks_zh_Hans.adoc @@ -0,0 +1,188 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1,toclevels=1] += 基准测试 +本节将 Boost.JSON 与两个广泛使用的具有类似功能的库进行性能比较:以高性能著称的 RapidJSON 和以功能丰富著称的JSON for Modern C++ 库(nlohmann/json)。bench 程序测量解析和序列化一组代表典型工作负载的 JSON 的吞吐量。评估的实现包括: + +.实现 +|=== +|Name|描述 + +| *boost(pool)* +| 使用 <> 解析输入,该资源针对解析后不进行后续修改的场景进行了优化。<> 对象在各次试验之间被复用,使得实现所分配的临时内存得以保留,从而提升性能。 + +| *boost* +| 使用 <> 解析输入,该资源基于标准 C++ 分配器,适用于通用场景,包括在解析后对文档进行修改。<> 对象在各次试验之间被复用,使得实现所分配的临时内存得以保留,从而提升性能。 + +| *rapidjson(pool)* +| 使用 https://rapidjson.org/classrapidjson_1_1_memory_pool_allocator.html[`MemoryPoolAllocator`] 的实例解析输入,该分配器针对“仅解析、不修改”的场景进行了优化。持有临时内存的 https://rapidjson.org/classrapidjson_1_1_generic_document.html[`Document`] 对象在多次测试之间不重复使用,否则内存消耗将无限增长,从而导致基准测试结果无效。 + +| *rapidjson* +| 使用 https://rapidjson.org/classrapidjson_1_1_crt_allocator.html[`CrtAllocator`] 的实例解析输入,该分配器使用标准 C++ 分配器,适用于通用场景,包括解析后对文档的修改。持有临时内存的 https://rapidjson.org/classrapidjson_1_1_generic_document.html[`Document`] 对象在多次测试之间不重复使用,否则内存消耗将无限增长,导致基准测试结果无效。 + +| *nlohmann* +| 使用静态成员函数 https://nlohmann.github.io/json/classnlohmann_1_1basic__json_ab330c13ba254ea41fbc1c52c5c610f45.html[`json::parse`] 解析输入,该函数使用默认的 `std` 分配器,适用于通用场景,包括解析后对文档的修改。该库未提供复用解析或序列化过程中所用临时存储的接口,因此无法在后续操作中重用这些临时内存。 +|=== + +== 方法论 +首先加载所有输入文件。随后,每种配置会运行足够多的试验次数,以确保总运行时间不少于 5 秒。每次试验会记录所用时间、调用次数(解析或序列化)以及传输的字节数,并以此生成一个样本,同时计算出以 MB/s(兆字节每秒)为单位的吞吐量。每种配置会生成多个样本(目前为五个),剔除其中非中间两个的样本后,对剩余的两个样本取平均值,作为该配置的最终基准测试结果。 + +输入文件位于 bench/data 目录,按如下方式布局: + +.输入文件 +|=== +|Name|Size|描述 + +| <> +| 125KB +| 来自 Apache Jenkins 安装的数据。 + +| <> +| 2.2MB +| 最大的文件,包含大量由两个浮点数坐标对组成的二元数组。 + +| <> +| 1.69MB +| 一个包含多种嵌套结构、数据类型和长度的大型 JSON。 + +| <> +| 64KB +| 一份来自 GitHub Events API 的数据导出。 + +| <> +| 3.25MB +| Google Summer of Code 2018 data. + +| <> +| 216KB +| 一个由大型对象组成的数组。 + +| <> +| 2.91MB +| 一个以 JSON 格式序列化的 three.js 示例模型。 + +| <> +| 707KB +| 一个表示三维网格的 JSON,包含大量浮点数。 + +| <> +| 1.54MB +| 添加了空格的 mesh.json。 + +| <> +| 147KB +| 一个仅包含浮点数的数组。 + +| <> +| 499KB +| 一个包含大量 Cyrillic(西里尔)字符的 JSON。 + +| <> +| 617KB +| 一份来自 Twitter API 的数据导出。 + +| <> +| 550KB +| 移除空格并将非 ASCII 字符替换为 Unicode 转义符的 twitter.json。 + +| <> +| 521KB +| 一份来自 Twitter API 的数据导出。 +|=== + +测试所用硬件:**Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz**, Windows 10, 32GB RAM. + +编译器及优化选项:gcc 8.1 (-O3), clang 12.0 (-O3), and msvc 19.26 (/O2) + +== 解析 + +=== 解析 apache_builds.json +image::images/parse_apache_builds.png[width=668,align="left"] + +=== 解析 canada.json +image::images/parse_canada.png[width=668,align="left"] + +=== 解析 citm_catalog.json +image::images/parse_citm_catalog.png[width=668,align="left"] + +=== 解析 github_events.json +image::images/parse_github_events.png[width=668,align="left"] + +=== 解析 gsoc-2018.json +image::images/parse_gsoc_2018.png[width=668,align="left"] + +=== 解析 instruments.json +image::images/parse_instruments.png[width=668,align="left"] + +=== 解析 marine_ik.json +image::images/parse_marine_ik.png[width=668,align="left"] + +=== 解析 mesh.json +image::images/parse_mesh.png[width=668,align="left"] + +=== 解析 mesh.pretty.json +image::images/parse_mesh_pretty.png[width=668,align="left"] + +=== 解析 numbers.json +image::images/parse_numbers.png[width=668,align="left"] + +=== 解析 random.json +image::images/parse_random.png[width=668,align="left"] + +=== 解析 twitter.json +image::images/parse_twitter.png[width=668,align="left"] + +=== 解析 twitterescaped.json +image::images/parse_twitterescaped.png[width=668,align="left"] + +=== 解析 update-center.json +image::images/parse_update_center.png[width=668,align="left"] + +== 序列化 + +=== 序列化 canada.json +image::images/serialize_canada.png[width=668,align="left"] + +=== 序列化 citm_catalog.json +image::images/serialize_citm_catalog.png[width=668,align="left"] + +=== 序列化 github_events.json +image::images/serialize_github_events.png[width=668,align="left"] + +=== 序列化 gsoc-2018.json +image::images/serialize_gsoc_2018.png[width=668,align="left"] + +=== 序列化 instruments.json +image::images/serialize_instruments.png[width=668,align="left"] + +=== 序列化 marine_ik.json +image::images/serialize_marine_ik.png[width=668,align="left"] + +=== 序列化 mesh.json +image::images/serialize_mesh.png[width=668,align="left"] + +=== 序列化 mesh.pretty.json +image::images/serialize_mesh_pretty.png[width=668,align="left"] + +=== 序列化 numbers.json +image::images/serialize_numbers.png[width=668,align="left"] + +=== 序列化 random.json +image::images/serialize_random.png[width=668,align="left"] + +=== 序列化 twitter.json +image::images/serialize_twitter.png[width=668,align="left"] + +=== 序列化 twitterescaped.json +image::images/serialize_twitterescaped.png[width=668,align="left"] + +=== 序列化 update-center.json +image::images/serialize_update_center.png[width=668,align="left"] diff --git a/doc/pages/comparison_zh_Hans.adoc b/doc/pages/comparison_zh_Hans.adoc new file mode 100644 index 0000000..76f082a --- /dev/null +++ b/doc/pages/comparison_zh_Hans.adoc @@ -0,0 +1,102 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#comparison, pagelevels=1,toclevels=1] += 与其他库的比较 + +:icon_good: pass:q[[.green]##✔##] +:icon_bad: pass:q[[.red]##✘##] + +C++ 中存在众多 JSON 库,但为了比较的目的,有三个在对比中尤为值得关注:https://rapidjson.org/[RapidJSON]、https://nlohmann.github.io/json/[JSON for Modern {cpp}](本文中称为 nlohmann's JSON, 或简称 nlohmann)以及https://github.com/lemire/simdjson[SIMD JSON]。 + +== 与nlohmann JSON的比较 + +值类型:https://github.com/nlohmann/json/blob/00cb98a3d170161711ab912ae6acefba31f29f75/include/nlohmann/json.hpp#L165[`nlohmann::basic_json`] + +[source] +---- +template< + template class ObjectType, + template class ArrayType, + class StringType, + class BooleanType, + class NumberIntegerType, + class NumberUnsignedType, + class NumberFloatType, + template class AllocatorType, + template class JSONSerializer + > +class basic_json +{ +private: + .... + friend ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; +... +---- + +该库采用了一种“大而全”(kitchen sink)的设计理念,包含大量功能,甚至涵盖一些使用场景非常小众的特性。其弱点在于:尽管众多模板参数提供了高度可配置性,却阻碍了编译器实现最优的性能优化。其结果是性能表现较差。这种对值类型各方面均可配置的能力,反而使其作为通用类型的适用性降低,形成了一种悖论效应。 + +* {icon_bad} `basic_json` 是一个类模板。各库必须在模板参数的选择上达成一致,才能实现互操作。 + +* {icon_bad} 过度定制化。我们很难想象将 `BooleanType` 设置为 `bool` 之外的其他类型的使用场景。 + +* {icon_bad} 关注点分离不佳。`basic_json` 容器声明不必要地将解析和序列化API混杂在一起。 + +* {icon_bad} 分配器支持有限。只允许无状态分配器,这排除了最重要的一类分配器,即基于本地内存池的实现。 + +* {icon_bad} 没有增量式解析,也没有增量式序列化。 + +* {icon_bad} 解析和序列化性能较差。 + +* {icon_good} 功能全面,包含 JSON Pointer、CBOR 等特性。 + +== 与RapidJSON的比较 + +值类型:https://github.com/Tencent/rapidjson/blob/bb5f966b9939d6cdfbac3462a0410e185099b3af/include/rapidjson/document.h#L608[`rapidjson::GenericValue`] + +[source] +---- +template > +class GenericValue; + +template +class GenericArray; + +template +class GenericObject; +---- + +* {icon_bad} 值类型不满足“正规性”要求。赋值具有破坏性,执行 `a = b` 等同于 `a = std::move(b)`,且不允许复制构造和复制赋值。 + +* {icon_bad} 对象类型没有哈希表或索引用于降低查找成本。 + +* {icon_bad} 分配器采用引用语义,容易引发生命周期问题。 + +* {icon_bad} 数组和对象类型的接口与标准库中的对应类型差异显著,且不符合惯用法。 + +* {icon_bad} 没有增量式解析,也没有增量式序列化。 + +* {icon_good} 解析和序列化性能优于大多数其他库。 + +=== 与SIMD JSON的比较 + +[source] +---- +class ParsedJson; +---- + +这是一种相当有趣的数据结构,专为与 SIMD 解析器协同工作而优化。它在目标使用场景下做出了非常优秀的设计选择。然而,由于其必要的限制,它不太适合作为通用类型。 + +* {icon_bad} 仅支持通过 `ParsedJson::BasicIterator` 进行顺序访问 + +* {icon_bad} 为只读类型,仅能通过所提供的 SIMD JSON 解析器进行填充。 + +* {icon_good} 为目前最快的JSON解析器。 diff --git a/doc/pages/conversion/alloc_zh_Hans.adoc b/doc/pages/conversion/alloc_zh_Hans.adoc new file mode 100644 index 0000000..0cab67b --- /dev/null +++ b/doc/pages/conversion/alloc_zh_Hans.adoc @@ -0,0 +1,32 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 分配控制 +由于<>会创建一个<>对象,用户可能希望控制其内存分配方式。因此,该函数具有一个可选的 <> 参数,用于为结果指定{ref_memory_resource}。 + +[NOTE] +<> 没有类似的参数,因为该函数不会创建 <> 对象。 + +由于转换结果是通过类型为`value&`的输出参数设置的,因此预期的<>会被正确传播。但用户仍需注意,避免意外创建使用默认 {ref_memory_resource} 的临时对象。 + +例如,考虑<>节中针对`ip_address`的`tag_invoke`的替代实现。 + +``` + +void +tag_invoke( const value_from_tag&, value& jv, ip_address const& addr ) +{ + jv = array{ b[0], b[1], b[2], b[3] }; +} + +``` + +该实现显式创建了一个 <>,而不是依赖于初始化列表的赋值。但该数组使用的是默认的 {ref_memory_resource},而不是`jv`所使用的内存资源。 + +为避免创建使用错误 {ref_memory_resource} 的临时对象,可以借助 <> 的成员函数 <>、<> 和 <>。 diff --git a/doc/pages/conversion/context_zh_Hans.adoc b/doc/pages/conversion/context_zh_Hans.adoc new file mode 100644 index 0000000..56de8d3 --- /dev/null +++ b/doc/pages/conversion/context_zh_Hans.adoc @@ -0,0 +1,98 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 上下文相关的转换 +此前在本节中,我们一直假定某个类型存在一种特定且合适的 JSON 表示形式。但实际情况并非总是如此。很多时候,同一个值在不同场景下需要以不同的 JSON 格式来表示。在 Boost.JSON 中,可以通过提供一个额外的参数——上下文(context)——来实现这一点。 + +我们来实现从`user_ns::ip_address`到JSON字符串的转换: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_1,indent=0] +---- + + +这些`tag_invoke`重载接受一个额外的`as_string`参数,用于将`ip_address`的这种特定表示形式与其他所有可能的表示形式区分开来。要使用这些重载,需要将`as_string`对象作为最后一个参数传递给 <> 或 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_2,indent=0] +---- + +请注意,如果对于给定类型和给定上下文没有专用的`tag_invoke`重载,实现将回退到不带上下文的重载。因此,可以轻松地将上下文相关的转换与库提供的转换结合起来: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_3,indent=0] +---- + +== 第三方类型的转换 +通常,您无法为第三方库或标准库中的类型提供转换函数,因为这需要将`tag_invoke`重载放入您无法控制的命名空间中。但借助上下文,您可以将这些重载放在自己的命名空间里。这是因为上下文会将其关联的命名空间添加到搜索`tag_invoke`重载的命名空间列表中。 + +例如,我们来使用 https://en.wikipedia.org/wiki/ISO_8601[ISO 8601]格式,对 https://en.cppreference.com/w/cpp/chrono/system_clock[``std::chrono::system_clock::time_point``] 实现转换。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_4,indent=0] +---- + +为简洁起见,反向转换在此省略。 + +这个新上下文的使用方式与本节前面介绍的`as_string`类似。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_5,indent=0] +---- + +上下文支持的一个特定用例是适配器库,这些库为来自不同库的类型定义JSON转换逻辑。 + +== 向转换函数传递数据 +到目前为止,我们使用的上下文都是空类。但上下文可以像普通类一样拥有数据成员和成员函数。转换函数的实现者可以利用这一点,在运行时对转换行为进行配置,或向转换函数传递特殊对象(例如分配器)。 + +我们来重写``system_clock::time_point``的转换,以支持`std::strftime`所支持的任意格式。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_6,indent=0] +---- + +这个`tag_invoke`重载允许我们在运行时更改日期转换的格式。另外请注意,`as_iso_8601`重载和`date_format`重载之间不存在歧义。您可以在程序中使用两者: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_7,indent=0] +---- + +== 组合上下文 +通常需要同时使用多个转换上下文。例如,考虑一个记录远程用户(通过 IP 地址标识)访问系统的日志,我们可以将其表示为 `std::vector>`。我们希望将``ip_address``和``time_point``都序列化为字符串,但这需要同时使用`as_string`和`as_iso_8601`两个上下文。要组合多个上下文,只需使用{std_tuple}。转换函数会从元组中选择第一个存在对应 `tag_invoke` 重载的元素,并调用该重载。与往常一样,不使用上下文的`tag_invoke`重载和库提供的通用转换也受支持。因此,我们的示例如下: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_8,indent=0] +---- + +在此代码片段中,`time_point`通过接受`as_iso_8601`的`tag_invoke`重载进行转换,`ip_address`通过接受`as_string`的`tag_invoke`重载进行转换,而{std_vector}则使用库提供的通用转换进行转换。 + +== 上下文与复合类型 +如前所示,库提供的通用转换会将上下文传递给嵌套对象的转换函数。因此,当您希望为特定上下文启用的某个复合类型提供自己的转换函数时,通常也需要这样做 + +考虑以下示例。如前一节所述,<>要求键类型满足<>。现在,假设您的键不是类字符串类型,但它们确实可以转换为<>。您可以通过上下文使此类映射也转换为对象。但如果您想同时为值使用另一个上下文,则需要一种方式将完整的组合上下文传递给映射元素。因此,您希望以下测试能够成功。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_9,indent=0] +---- + +为此,您必须使用另一个`tag_invoke`重载。这次,它必须是一个函数模板,并且应包含两个上下文参数。第一个参数是用于区分该特定重载的具体上下文;第二个参数是传递给 <> 或<>的完整上下文。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_10,indent=0] +---- diff --git a/doc/pages/conversion/custom_zh_Hans.adoc b/doc/pages/conversion/custom_zh_Hans.adoc new file mode 100644 index 0000000..557e560 --- /dev/null +++ b/doc/pages/conversion/custom_zh_Hans.adoc @@ -0,0 +1,74 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 自定义转换 +Boost.JSON 提供了两种机制用于自定义 <> 与用户类型之间的转换。一种机制涉及特化类型特征(type traits)。另一种机制更强大,需要定义`tag_invoke`的重载。本节将进一步解释这两种机制。 + +== 转换特征 +之前已经介绍了一些转换类型特征,例如 <> 或 <>。库会依次尝试这些特征,并使用与第一个匹配特征对应的实现。然而,在某些情况下,某个类型可能会匹配到优先级更高的特征,而用户实际希望将其归入优先级更低的类别。如果发生这种情况,用户可以针对该类型特化那个不应匹配的特征,使其等同于`std::false_type`。 + +考虑以下类型: + +[source] +---- +include::../../../test/doc_types.hpp[tag=snippet_conv_spec_trait1,indent=0] +---- + +它同时暴露了序列(sequence)API 和元组(tuple)API。然而,从 <> 转换到 user_ns::ip_address 时无法使用序列的实现,因为序列类型的转换会先构造一个空对象,再逐个填充元素,而 ip_address 的大小固定为 4。相比之下,元组转换是合适的。唯一的问题在于 <> 的优先级低于 <>。为绕过此问题,用户只需特化 <>,使其对 ip_address 不匹配即可。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_spec_trait2,indent=0] +---- + +== `tag_invoke`重载 +第二种、更强大的方法是自行提供转换实现。在 Boost.JSON 中,这是通过定义`tag_invoke`函数的重载来实现的(该机制的优点详见 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf[C++提案P1895])。本质上,`tag_invoke` 通过参数依赖查找(ADL)在调用点寻找可用的重载,从而为自定义扩展点提供统一接口。顾名思义,一个标签类型作为参数传递,用于: + +* 1. 排除与该特定自定义点无关的候选函数,并且 + +* 2. 将用户定义的类型嵌入到参数列表中(例如,通过使用像`value_to_tag`这样的标签类型模板),以便在执行名称查找时检查其http://eel.is/c++draft/basic.lookup.argdep#2[关联的命名空间和实体]。 + +这样做的效果是能够找到用户提供的`tag_invoke`重载,即使它们在调用函数定义之后(在词法上)声明。 + +由 <> 调用的 tag_invoke 重载具有如下形式: + +``` + +void tag_invoke( const value_from_tag&, value&, T ); + +``` + +而由 <> 调用的`tag_invoke`重载采用以下形式: + +``` + +T tag_invoke( const value_to_tag< T >&, const value& ); + +``` + +如果我们用这种方法手动实现`user_ns::ip_address`的转换,它将如下所示: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_1,indent=0] +---- + +由于被转换的类型已嵌入函数签名中,用户提供的重载对参数依赖查找(ADL)可见,并会在执行转换时成为候选函数: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_2,indent=0] +---- + +用户可以自由地将具有自定义转换的类型与具有库提供转换的类型组合使用,库能正确处理它们: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_3,indent=0] +---- diff --git a/doc/pages/conversion/direct_zh_Hans.adoc b/doc/pages/conversion/direct_zh_Hans.adoc new file mode 100644 index 0000000..24f0d50 --- /dev/null +++ b/doc/pages/conversion/direct_zh_Hans.adoc @@ -0,0 +1,26 @@ +//// +Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 直接转换 +对于大型输入,先解析到库的容器中,再通过 <> 进行转换(或反之,先通过 <> 转换,再从 <> 序列化)可能代价过高。针对这类场景,库提供了可直接将数据解析到用户提供的对象中、或直接从用户提供的对象进行序列化的组件。 + +这种方法的缺点在于,不支持完全自定义的类型表示,仅支持库提供的转换。此外,所有需通过解析进行填充的对象都必须是可默认构造的类型——这不仅包括顶层对象,也包括容器的元素、所描述`struct`的成员以及变体类型的备选项。 + +尽管如此,如果你的类型是可默认构造的,并且不需要 <> 和 <> 所提供的自定义能力,那么使用直接转换可以获得显著的性能提升。 + +直接解析是由<> 系列函数执行的。库提供了接受<<> 或`std::istream`的重载,并且可以通过抛出异常或设置错误码来报告错误。 + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_parse_into_1,indent=0] +---- + +如果需要将增量解析与直接解析结合使用,可以借助 <>。`parser_for`是<> 的一个实例化,用于将数据解析到类型为`T`的对象中,并且是<>在底层所使用的。 + +直接序列化不需要任何特殊组件,可直接使用常规的 <> 和<>。 diff --git a/doc/pages/conversion/forward_zh_Hans.adoc b/doc/pages/conversion/forward_zh_Hans.adoc new file mode 100644 index 0000000..30a19aa --- /dev/null +++ b/doc/pages/conversion/forward_zh_Hans.adoc @@ -0,0 +1,34 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 避免物理依赖 +一些用户(尤其是库作者)可能希望为其类型与 <> 提供转换功能,但同时又希望避免让自己的库依赖 Boost.JSON。借助一些前向声明,即可实现这一目标。 + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_1,indent=0] +---- + +请注意,<> 是通过输出参数声明的,而非返回其结果。该重载正是为这一使用场景而设计的。 + +之后应提供 `tag_invoke` 重载的定义。这些重载必须是模板,因为 <> 仅是前向声明的,因此是一个不完整类型。 + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_2,indent=0] +---- + +如前所述,我们更倾向于为 <> 定义不抛异常的 `tag_invoke` 重载,而不是为 <> 定义抛异常的重载,因为后者可以在无性能损失的情况下回退到前者。 + +上下文相关转换的前向声明方式与此非常相似: + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_3,indent=0] +---- diff --git a/doc/pages/conversion/guidelines_zh_Hans.adoc b/doc/pages/conversion/guidelines_zh_Hans.adoc new file mode 100644 index 0000000..15b5a3a --- /dev/null +++ b/doc/pages/conversion/guidelines_zh_Hans.adoc @@ -0,0 +1,21 @@ +//// +Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 转换自定义指南 +由于选项众多,为类型选择最合适的转换自定义方式可能并不容易。本节将讨论这些选项,并给出不同场景下的选用建议。 + +首要建议是:除非生成的格式不符合需求,否则应优先使用库提供的转换,而非自行实现自定义转换。如果库错误地推断了转换类别,你可以通过将相关特征特化为继承自 `std::false_type` 来排除该转换。 + +如果库提供的转换对你适用,你可以选择使用直接转换。但这同时也要求你的许多类型必须是可默认构造的。 + +接下来需要考虑的是:您的转换是仅供内部使用,还是面向团队以外的用户。如果您的用户属于外部人员,那么他们最终将决定这些转换的使用条件;反之,对于内部库和应用程序,您可以完全掌控其使用条件。 + +如果您的用户是外部的,那么是否允许抛出异常是由他们而非您来决定的。因此,在这种情况下,最好使用不抛异常的 `tag_invoke` 重载。此外,在自定义复合类型的转换时,应始终使用带有两个上下文参数的 `tag_invoke` 重载。这将确保上下文能正确传递给复合类型的各个元素,同时也支持从元素转换中传播异常。 + +最后值得一提的是,由于可以在不引入对库的二进制依赖的前提下提供到 JSON 容器的转换,因此您无需将此类依赖强加给您的用户。这一点对于那些仅需与 Boost.JSON 进行辅助性互操作的库尤为重要。 diff --git a/doc/pages/conversion/nothrow_zh_Hans.adoc b/doc/pages/conversion/nothrow_zh_Hans.adoc new file mode 100644 index 0000000..cf3957f --- /dev/null +++ b/doc/pages/conversion/nothrow_zh_Hans.adoc @@ -0,0 +1,40 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 不抛异常的转换 +对于不希望抛出异常的场景,Boost.JSON 还提供了 <> 的不抛异常版本,即函数模板 <>。它返回 {ref_result},这是一个特化的变体类型,要么持有转换得到的值,要么持有 {ref_error_code}。 + +[NOTE] +对于 <>; 没有对应的不抛异常版本。这仅仅是因为我们尚未遇到需要 <> 向调用者报告错误的情况。 + +该库为 <> 所支持的所有类型类别均提供了不抛异常的转换。如果用户希望将其用于自定义类型,则需要提供如下形式的 `tag_invoke` 重载: + +``` + +result_for::type tag_invoke( const try_value_to_tag< T >&, const value& ); + +``` + +对于 <> 节中所述的 `ip_address` 类,该重载可能如下所示: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_nothrow_1,indent=0] +---- + +该重载使我们能够将 `ip_address` 与 <> 一起使用。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_nothrow_2,indent=0] +---- + +如果对某个类型使用 <>,而该类型没有本节所述形式的 `tag_invoke` 重载,但存在适用于 <> 的重载,那么库仍会尝试执行转换:它会调用抛异常的重载,并试图将抛出的任何异常转换为 {ref_error_code}。但请注意,这种方式的性能很可能不如专门提供的不抛异常重载。 + +反之亦然:如果存在适用于<> 的 tag_invoke 重载,但没有适用于 <> 的重载,那么调用 <> 时会转而调用不抛异常的重载,随后根据返回的 {ref_error_code} 构造一个 {ref_system_error} 并抛出。由于存在上述回退机制,建议用户在计划使用 <> 时,优先提供本节所述的不抛异常重载,而非抛异常的重载。 diff --git a/doc/pages/conversion/overview_zh_Hans.adoc b/doc/pages/conversion/overview_zh_Hans.adoc new file mode 100644 index 0000000..1b505af --- /dev/null +++ b/doc/pages/conversion/overview_zh_Hans.adoc @@ -0,0 +1,124 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2021 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#conversion] += 值转换 +尽管 <> 容器便于创建临时结构,但通常仍需在 JSON 与用户自定义类型或标准库类型之间进行转换。 + +函数模板 <> 提供了从类型 `T` 构造 <> 的接口。函数模板 <> 则执行相反方向的转换,从类型 `T` 转换为 <>。两者均支持多种不同的 https://en.cppreference.com/w/cpp/language/types[基础类型](如 `int` 或 `double`)、标准库类型(如 `std::string` 或 `std::vector`),并可扩展以支持用户定义类型。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_1,indent=0] +---- + +对于类型 `T`,将从以下类别列表中选择合适的转换方式,并采用第一个匹配的类别。 + +.转换类别 +[%autowidth, cols=4] +|=== +|T 的类别|注释|`value_from` 行为|`value_to` 行为 + +|自定义转换。 +| +|自定义行为。 +|自定义行为。 + +|Boost.JSON 容器。 +| +|结果等于输入值。 +|结果等于输入值。 + +|`bool` +| +|结果等于输入值。 +|结果等于输入值。 + +|https://en.cppreference.com/w/cpp/types/is_arithmetic[算术类型] +| +a| 结果为一个数值,等于输入值且类型为: + +* 若 `T` 为有符号整数,则为 `std::int64_t`;或 +* 若 `T` 为无符号整数,则为 `std::uint64_t`;或 +* 否则为 `double`。 +|结果通过 <> 创建。 + +|满足 <> 的类型 +|适用于类似 {std_monostate} 的类型。 +|结果为空值。 +|结果将被默认构造。 + +|满足 <> 的类型。 +|`char` 序列,例如 `std::string`。 +|结果为 <>。 +|结果由 <> 构造。 + +|满足 <> 的类型。 +|`std::variant` 及类似类型,例如 `boost::variant2::variant`。 +|结果等同于对活跃的变体备选项进行转换所得的结果。 +|结果包含首个转换成功的备选项。 + +|满足 <> 的类型 +| +|若输入值为空,则结果为 `null`;否则等同于对可选类型内部存储对象的转换结果。 +|若输入值为 `null`,则结果为默认构造;否则结果由输入值转换为可选类型内部存储类型的结果构造。 + +|满足 <> 的类型。 +|具有类字符串键的一对一映射(例如 `std::map`)。 +|结果为 <>。 +|结果为默认构造,元素在末尾通过 `insert` 被插入。 + +|满足 <> 的类型。 +|元素序列,例如 `std::vector`。 +|结果是一个 <>。 +|结果为默认构造,元素在末尾通过 `insert` 被插入。 + +|满足 <> 的类型。 +|一种大小固定的异构序列,例如 `std::tuple` 和 `std::pair`。 +|结果是一个 <>。 +|结果通过将数组元素作为构造函数参数进行构造。 + +|满足 <> 的类型 +| +|结果是一个 <>,其键为所描述成员的名称。 +|结果将被默认构造,并为其所描述的成员赋予相应的值。 + +|满足 <> 的类型 +| +|如果输入值等于某个已描述的枚举项,则结果为一个包含该枚举项名称的 <>;否则,结果等同于将输入值转换为其底层类型后的值。 +|结果为与输入 <> 对应的已描述枚举项。 + +|满足 <> 的类型。 +|`std::filesystem::path` 及类似类型,例如 `boost::filesystem::path`。 +|结果等于 `path::generic_string` 的结果。 +|结果由两个指向 `const char` 的指针构造而成。 +|=== + +对于复合类型(如序列、元组、已描述的类等),对其所包含对象的转换会递归应用。例如: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_recursive,indent=0] +---- + +此处,该映射被转换为一个 <>,因为它匹配 <>。其每个键被转换为一个 <>(因为 `std::string` 匹配 <>),而每个值被转换为一个 <>(因为 `std::pair` 匹配 <>)。最后,每对元素分别被转换为一个 `std::int64_t` 类型的数值和一个 `bool` 值。 + +:leveloffset: +1 + +include::custom.adoc[] +include::nothrow.adoc[] +include::alloc.adoc[] +include::context.adoc[] +include::forward.adoc[] +include::direct.adoc[] +include::guidelines.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/definitions_zh_Hans.adoc b/doc/pages/definitions_zh_Hans.adoc new file mode 100644 index 0000000..56d6e4d --- /dev/null +++ b/doc/pages/definitions_zh_Hans.adoc @@ -0,0 +1,44 @@ +//// +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +:ref_memory_resource: pass:q[https://boost.org/doc/libs/latest/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1memory__resource.html[`memory_resource`]] +:ref_polymorphic_allocator: pass:q[https://boost.org/doc/libs/latest/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1polymorphic__allocator.html[`polymorphic_allocator`]] +:ref_error_category: pass:q[https://boost.org/doc/libs/latest/libs/system/doc/html/system.html#ref_error_category[`error_category`]] +:ref_error_code: pass:q[https://boost.org/doc/libs/latest/libs/system/doc/html/system.html#ref_error_code[`error_code`]] +:ref_error_condition: pass:q[https://boost.org/doc/libs/latest/libs/system/doc/html/system.html#ref_error_condition[`error_condition`]] +:ref_result: pass:q[https://boost.org/doc/libs/latest/libs/system/doc/html/system.html#ref_resultt_e[`result`]] +:ref_system_error: pass:q[https://boost.org/doc/libs/latest/libs/system/doc/html/system.html#ref_system_error[`system_error`]] + +:req_Allocator: pass:q[https://en.cppreference.com/w/cpp/named_req/Allocator[__Allocator__]] +:req_CopyAssignable: pass:q[https://en.cppreference.com/w/cpp/named_req/CopyAssignable[__CopyAssignable__]] +:req_CopyConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/CopyConstructible[__CopyConstructible__]] +:req_Copyable: pass:q[https://en.cppreference.com/w/cpp/concepts/copyable[__Copyable__]] +:req_DefaultConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[__DefaultConstructible__]] +:req_Hash: pass:q[https://en.cppreference.com/w/cpp/named_req/Hash[__Hash__]] +:req_InputIterator: pass:q[https://en.cppreference.com/w/cpp/named_req/InputIterator[__LegacyInputIterator__]] +:req_ForwardIterator: pass:q[https://en.cppreference.com/w/cpp/named_req/ForwardIterator[__LegacyForwardIterator__]] +:req_MoveAssignable: pass:q[https://en.cppreference.com/w/cpp/named_req/MoveAssignable[__MoveAssignable__]] +:req_MoveConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/MoveConstructible[__MoveConstructible__]] +:req_Regular: pass:q[https://en.cppreference.com/w/cpp/concepts/regular[__Regular__]] +:req_Swappable: pass:q[https://en.cppreference.com/w/cpp/named_req/Swappable[__Swappable__]] +:req_SequenceContainer: pass:q[https://en.cppreference.com/w/cpp/named_req/SequenceContainer[__SequenceContainer__]] + +:std_array: pass:q[https://en.cppreference.com/w/cpp/container/array[`std::array`]] +:std_initializer_list: pass:q[https://en.cppreference.com/w/cpp/utility/initializer_list[`std::initializer_list`]] +:std_complex: pass:q[https://en.cppreference.com/w/cpp/numeric/complex[`std::complex`]] +:std_hash: pass:q[https://en.cppreference.com/w/cpp/utility/hash[`std::hash`]] +:std_memory_resource: pass:q[https://en.cppreference.com/w/cpp/memory/memory_resource[`std::pmr::memory_resource`]] +:std_monostate: pass:q[https://en.cppreference.com/w/cpp/utility/variant/monostate[`std::monostate`]] +:std_ostream: pass:q[https://en.cppreference.com/w/cpp/io/basic_ostream[`std::ostream`]] +:std_polymorphic_allocator: pass:q[https://en.cppreference.com/w/cpp/memory/polymorphic_allocator[`std::pmr::polymorphic_allocator`]] +:std_string: pass:q[https://en.cppreference.com/w/cpp/string/basic_string[`std::string`]] +:std_unordered_map: pass:q[https://en.cppreference.com/w/cpp/container/unordered_map[`std::unordered_map`]] +:std_uses_allocator: pass:q[https://en.cppreference.com/w/cpp/memory/uses_allocator[`std::uses_allocator`]] +:std_vector: pass:q[https://en.cppreference.com/w/cpp/container/vector[`std::vector`]] +:std_tuple: pass:q[https://en.cppreference.com/w/cpp/utility/tuple[`std::tuple`]] diff --git a/doc/pages/dom/array_zh_Hans.adoc b/doc/pages/dom/array_zh_Hans.adoc new file mode 100644 index 0000000..8d40e9b --- /dev/null +++ b/doc/pages/dom/array_zh_Hans.adoc @@ -0,0 +1,47 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_array] += `array` +一个 <> 存储一个 <> 的实例,作为 JSON 数组的底层表示。__array__ 类型的实例与存储 <> 的 {std_vector} 功能完全相同。此外,所有插入容器的值将使用与容器本身相同的 <>。 + +可以使用 <> 构造一个空数组,而不会产生任何内存分配。也可以显式指定一个 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_1,indent=0] +---- + +可以使用初始化列表来构造带有初始内容的对象。这些构造函数可能会分配内存并抛出异常: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_2,indent=0] +---- + +或者,也可以在构造之后插入元素: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_3,indent=0] +---- + +与标准库中的对应容器类似,元素可通过从 0 开始的索引直接访问:使用 <<> 进行带边界检查的访问,或使用 <> 进行不带边界检查的访问: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_4,indent=0] +---- + +有关所有可用成员函数和嵌套类型的完整列表,请参阅 <> 的参考页面。 + +== 格式化输出 + +当将 <> 输出到 {std_ostream} 时,结果是一个符合 JSON 规范的有效 JSON:数组将以方括号括起,并以逗号分隔其中的值。 diff --git a/doc/pages/dom/init_lists_zh_Hans.adoc b/doc/pages/dom/init_lists_zh_Hans.adoc new file mode 100644 index 0000000..379ff08 --- /dev/null +++ b/doc/pages/dom/init_lists_zh_Hans.adoc @@ -0,0 +1,87 @@ +//// +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 初始化列表 +初始化列表可用于构造或赋值一个<>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_1,indent=0] +---- + +简单的初始化列表会生成一个 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_2,indent=0] +---- + +初始化列表可以嵌套。以下示例中,我们构造了一个以数组作为元素的数组: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_3,indent=0] +---- + +当一个包含两个元素的初始化列表嵌套在外部初始化列表中时,尚不明确它表示的是一个 <> 还是一个 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_4,indent=0] +---- + +在这种情况下,如果每个元素均由一个字符串后跟一个值组成,则外层的初始化列表会被解释为一个 <>;否则,它会被解释为一个 <>。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_5,indent=0] +---- + +要手动消除歧义,请使用显式构造函数: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_6,indent=0] +---- + +初始化列表可用于明确地构造或赋值一个<> 或 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_7,indent=0] +---- + +同样,用于 <> 的初始化列表始终会被解释为 <>。此时,初始化列表必须由键值对组成。例如,以下代码无法编译,因为` `1 无法转换为字符串: + +[source] +---- +object jo = { { 1, 0.39 }, { "venus", 0.72 }, { "earth", 1 } }; +---- + +当初始化一个 <> 或 <> 时,要求初始化列表被解释为对应类型的规则仅适用于最外层的初始化列表;后续嵌套的元素将遵循常规的歧义解析规则。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_8,indent=0] +---- + +在初始化时,右值元素将被移动。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_9,indent=0] +---- + +[WARNING] +==== +请勿创建类型为 {std_initializer_list} 的变量。这可能导致临时对象在初始化列表被使用之前就被销毁。 +==== + +在所有情况下,通过初始化列表构造的 <>、<> 或 <> 所拥有的 <> 都会递归地传播到每个元素。 diff --git a/doc/pages/dom/nested_access_zh_Hans.adoc b/doc/pages/dom/nested_access_zh_Hans.adoc new file mode 100644 index 0000000..f8d0a5a --- /dev/null +++ b/doc/pages/dom/nested_access_zh_Hans.adoc @@ -0,0 +1,45 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#nested_access] += 访问深层嵌套元素 +为了便于便捷地获取和修改 <> 对象中深层嵌套的元素,该库实现了 https://datatracker.ietf.org/doc/html/rfc6901[RFC 6901 (JSON Pointer)]: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_1,indent=0] +---- + +这在不希望抛出异常的场景中尤为有用。例如,对比以下两种方式: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_2,indent=0] +---- + +与 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_3,indent=0] +---- + +该库还支持修改和添加深层嵌套的元素。函数 <> 以与 <> 类似的方式遍历值,但在某些情况下还能创建缺失的元素: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_4,indent=0] +---- + +具体行为由一个类型为 <> 的可选参数控制。例如,以下是使用不同选项的相同示例: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_5,indent=0] +---- diff --git a/doc/pages/dom/numbers_zh_Hans.adoc b/doc/pages/dom/numbers_zh_Hans.adoc new file mode 100644 index 0000000..bf7c78f --- /dev/null +++ b/doc/pages/dom/numbers_zh_Hans.adoc @@ -0,0 +1,62 @@ +//// +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_numbers] += 数字 +JSON数字使用`std::int64_t`、`std::uint64_t`和`double`表示。当 <> 从无符号整数构造时,其<>将为`kind::uint64`。同样,从有符号整数构造的<> 将具有`kind::int64`,若从浮点类型构造则为`kind::double_`: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_1,indent=0] +---- + +访问<>内包含的数字时,所使用的函数必须与该值的<>完全匹配;不会执行任何转换。例如,如果在一个包含`std::uint64_t` 的 <> 上调用 `as_double`,将抛出异常。类似地,函数 `if_double` 将返回`nullptr`,而调用 `get_double` 将导致未定义行为: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_2,indent=0] +---- + +若已知 <> 包含数字,但不确定其 <>,可使用 `value::to_number` 将该<> 转换为算术类型: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_3,indent=0] +---- + +若<>不包含数字,或当转换目标为整数类型`T`且数字无法被`T`精确表示时,将导致转换失败。否则,结果是通过`static_cast`将数字转换为`T`所得的值: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_4,indent=0] +---- + +在无法使用异常的场合,可改用接受{ref_error_code}的`value::to_number`重载,其语义与抛出异常版本完全相同: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_5,indent=0] +---- + +在解析 JSON 文档时,数字的表示类型并未显式指定,而必须根据其值来确定。通常,解析器会选择能够精确存储文档中所出现数字的最佳类型。对于整数(即不带小数部分或指数的数字),若其值无法用 `std::uint64_t` 或 `std::int64_t` 表示,则会以 `double` 类型存储,以保留其数量级。 + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_6,indent=0] +---- + +更正式地,如果该数字: + +* 包含小数点,或 +* 包含指数,或 +* 为负数且其值小于`INT64_MIN`,或 +* 为正数且其值大于`UINT64_MAX`, + +则其类型为`double`。否则,若数字为正数且其值大于`INT64_MAX`,则其类型为`std::uint64_t`。其余所有数字均被解析为`std::int64_t`。 diff --git a/doc/pages/dom/object_zh_Hans.adoc b/doc/pages/dom/object_zh_Hans.adoc new file mode 100644 index 0000000..93bd7ae --- /dev/null +++ b/doc/pages/dom/object_zh_Hans.adoc @@ -0,0 +1,73 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_object] += `object` +<> 使用 <> 的实例作为 JSON 对象的底层表示。<> 类型的实例是一种关联容器,存储键值对,其中键为 <>,映射的值类型为 <>。此类容器以标准映射(map)为模型,具有以下特性: + +* 元素以 <> 的实例形式连续存储。 + +* 迭代器是普通指针,在插入或删除操作时可能会失效。 + +* 只要不发生删除操作,插入顺序即被保留。 + +* 所有插入的值将使用与容器本身相同的 {ref_memory_resource}。 + +可以使用 <> 构造一个空对象,而不产生任何内存分配。也可以显式指定一个 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_1,indent=0] +---- + +由两元素键值对组成的初始化列表可用于构造带有初始内容的对象。这些构造函数可能会分配内存并抛出异常: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_2,indent=0] +---- + +或者,也可以在构造之后插入元素: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_3,indent=0] +---- + +与 `std` 对应容器类似,可通过 <> 使用边界检查直接按键访问元素,或通过 <> 进行无边界检查访问(若键不存在则创建空元素): + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_4,indent=0] +---- + +容器内部通过键计算哈希表,使得查找操作的平均时间复杂度为常数级别。 + +[WARNING] +==== +与传统的基于节点的容器(如 `std::set`)不同,插入和擦除操作不保证引用稳定性或迭代器稳定性。 + +例如: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_5,indent=0] +---- + +在向 `obj` 添加一个新值后继续使用 `arr`,会导致未定义行为。 +==== + +完整成员函数及嵌套类型列表请参阅 <> 的参考页。 + +与 `std::pair` 类似,<> 类型可用于 {cpp}17 的结构化绑定。为此专门提供了 `std::tuple_size`、`std::tuple_element` 的特化及 <> 的重载。 + +== 格式化输出 + +当 <> 被格式化输出到 {std_ostream} 时,结果将是有效的 JSON。即根据 JSON 规范,对象将以花括号和逗号分隔的键/值对列表形式输出。 diff --git a/doc/pages/dom/overview_zh_Hans.adoc b/doc/pages/dom/overview_zh_Hans.adoc new file mode 100644 index 0000000..a57511a --- /dev/null +++ b/doc/pages/dom/overview_zh_Hans.adoc @@ -0,0 +1,54 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom] += 文档模型 +在本库中,以下类型实现了用于在内存中表示 JSON 数据的容器: + +.容器类型 +[%autowidth] +|=== +|Type|描述 + +| <> +| 一种支持动态大小和快速随机访问的 JSON 值序列容器。其接口和性能特性类似于 {std_vector}。 + +| <> +| 一种具有唯一键的键值对关联容器,其中键为字符串,映射类型为 JSON 值。查找、插入和移除操作具有平均常数时间复杂度。此外,元素在内存中连续存储,支持缓存友好的迭代。 + +| <> +| 一个连续的字符范围。该库假定字符串内容仅包含有效的 UTF-8 编码。 + +| <> +| 一种特殊的变体类型,可持有六种标准 JSON 数据类型中的任意一种。 +|=== + +这些容器将在接下来的章节中深入探讨。 + +[NOTE] +==== +文中所使用的示例代码和标识符均假定以下声明已生效: + +[source] +---- +#include +using namespace boost::json; +---- +==== + +:leveloffset: +1 +include::value.adoc[] +include::string.adoc[] +include::array.adoc[] +include::object.adoc[] +include::numbers.adoc[] +include::init_lists.adoc[] +include::nested_access.adoc[] +:leveloffset: -1 diff --git a/doc/pages/dom/string_zh_Hans.adoc b/doc/pages/dom/string_zh_Hans.adoc new file mode 100644 index 0000000..d2bd0c5 --- /dev/null +++ b/doc/pages/dom/string_zh_Hans.adoc @@ -0,0 +1,76 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_string] += `string` + +可修改的字符序列通过 <> 类型的对象表示。 + +<> 的接口和功能与 {std_string} 相同,但有以下例外: + +* <>不是一个类模板, +* <> 使用`char`作为其字符类型, +* 字符串操作中冗余的重载已被基于 <> 的接口所取代, +* 允许访问`[size(), capacity())`范围内的字符, +* 使用<>代替{req_Allocator},且 +* 保证采用小缓冲区优化,从而避免为短字符串分配内存。 + +通过增强的接口,所有需要输入字符串的操作均以单一重载形式实现,其参数类型为 <>,并可接受大多数类字符串对象。诸如以空字符结尾的字符指针、`std::string`、<>、字符串子范围以及可转换为 <> 的对象,均可传递给这些函数。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_4,indent=0] +---- + +更正式地说,`std::string` 中接受以下任一参数组合作为输入字符串的成员函数重载: + +* 一个`std::string`参数,或 +* 一个`std::string`参数和两个指定子字符串的`size_type`参数,或 +* 一个可转换为 <> 类型的参数,或 +* 一个可转换为<>类型的参数和两个指定子字符串的`size_type`参数,或 +* 一个`const_pointer`参数,或 +* 一个`const_pointer`类型的参数和一个指定字符串长度的`size_type`参数 + +被一个接受 <> 参数的重载所替代。 + +这一设计从接口中移除了多个冗余的重载。例如,`std::string::insert` 原本有 11 个重载,而在 <> 中被减少到仅 3 个,同时仍提供完全相同的功能。除此之外,接受 `std::initializer_list` 参数的重载也被移除。这类重载用途有限,因为它们本质上只是对字符数组的封装,且语法效率低下: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_3,indent=0] +---- + +随着用于指定子字符串参数的重载被移除,库提供了一个成员函数`subview`, 用于返回一个 <>,从而支持高效的子字符串操作: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_2,indent=0] +---- + +<> 可使用 <>(默认构造资源) 进行构造,且不会产生任何内存分配。或者,也可以显式提供一个 <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_1,indent=0] +---- + +== 格式化输出 + +当<>被格式化到{std_ostream}时,结果是一个有效的JSON。即输出结果将被双引号括起来,并且内容会根据JSON规范进行正确转义。 + +== 访问超出`size()`范围的存储空间 + +<> 直接支持访问其在`[size(), capacity())`范围内的存储空间。这可用于高效地将字符串从多个部分组装起来。字符串组装完成后,可调用成员函数 <> 来更新字符串的大小并插入空终止符。例如: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_5,indent=0] +---- diff --git a/doc/pages/dom/value_zh_Hans.adoc b/doc/pages/dom/value_zh_Hans.adoc new file mode 100644 index 0000000..e655858 --- /dev/null +++ b/doc/pages/dom/value_zh_Hans.adoc @@ -0,0 +1,223 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_value] += `value` +JSON文档在内存中表示为<>的实例:一种{req_Regular}类型,满足{req_DefaultConstructible}、{req_CopyConstructible}、{req_CopyAssignable}、{req_MoveConstructible}、{req_MoveAssignable}以及许多分配器感知容器的要求。它在内部实现为一个https://en.wikipedia.org/wiki/Tagged_union[__variant__](变体),并且可以动态存储六种已定义的JSON值类型中的任意一种: + +* **空值**:一种https://en.cppreference.com/w/cpp/utility/variant/monostate[__monostate__](单态)值,等同于`nullptr`。 + +* **布尔值**:布尔类型,取值为`true`或`false`。 + +* **数值**:整型或浮点型值。 + +* **字符串**:由零个或多个Unicode字符组成的序列,类似于{std_string}。 + +* **数组**:值的有序列表,类似于{std_vector}。 + +* **对象**:名称/值对的集合,也称为https://en.wikipedia.org/wiki/Associative_array[__关联数组__]。 + +== 处理值 +从`nullptr`构造或默认构造的 <> 表示一个空的JSON元素: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_1,indent=0] +---- + +成员函数<>可用于查询值中存储的种类。或者,也可以使用诸如<>、<> 等成员函数来检查值是否为特定种类: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_2,indent=0] +---- + +诸如 <> 的函数在值为对象时返回指向该对象的指针,否则返回空指针。这使得它们既可用于上述的布尔上下文,也可用于赋值或条件表达式中,以捕获指针的值: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_3,indent=0] +---- + +<> 构造之后,其类型可根据赋值内容或通过调用<> 或 <> 等函数来改变。如果赋值成功,即未引发任何异常,则该值将被替换。否则,原值保持不变。所有可能修改值失败的操作均提供强异常安全保证。 + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_4,indent=0] +---- + +下表列出了所有用于判断和访问 <> 内容的方法: + +.<>访问器 +[%autowidth, cols=8] +|=== +|种类 +|表示形式 +|置入 +|种类测试 +|指针访问 +|`result`访问 +|受检访问 +|非受检访问 + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/types/integer[`std::int64_t`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/types/integer[`std::uint64_t`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/types[`double`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/types[`bool`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/nullptr[`std::nullptr_t`] +|<> +|<> +^|— +|<> +^|— +^|— + +|=== + +<> 的置入成员函数返回对底层表示形式的类型化引用。例如,前例中对 <> 的调用返回一个 <> 。下表列出了每种 kind 对应的底层类型: + +|=== +|Kind|Type|描述 + +| <> +| <> +| 一个关联数组,其键为字符串,值为 <> 元素,接口类似于 {std_unordered_map},并保留插入顺序。 + +| <> +| <> +| 一个<>元素的有序列表,其接口类似于{std_vector}。 + +| <> +| <> +| 一个采用https://en.wikipedia.org/wiki/UTF-8[__UTF-8__]编码的https://en.wikipedia.org/wiki/Unicode[Unicode]字符https://en.wikipedia.org/wiki/String_(computer_science)[字符串],其接口类似于{std_string}。 + +| <> +| `std::int64_t` +| 64位有符号整数。 + +| <> +| `std::uint64_t` +| 64位无符号整数。 + +| <> +| `double` +| 一个`double`,用于保存浮点型值。 + +| <> +| https://en.cppreference.com/w/cpp/keyword/bool[`bool`] +| 一个`bool`,用于保存`true`或`false`。 + +| <> +^| — +| 一个表示空值的monostate值。 +|=== + +置入操作的返回值可用于执行赋值,或捕获对底层元素的引用以供后续检查或修改: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_5,indent=0] +---- + +如果已知 <> 的 <>,可使用诸如 <> 或 <> 等函数,在不改变现有值的情况下获取对底层表示的引用: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_6,indent=0] +---- + +然而,如上所示,如果 <> 中的实际类型与函数签名所指定的类型不匹配,这些函数会抛出异常。这一行为可作为一种简洁的验证形式:以预期类型直接访问值,若 JSON 结构无效,则通过捕获异常进行处理。 + +我们可以通过请求一个可能为空的指针(而非引用)来查询值中特定种类的底层表示形式,而不会抛出异常。这里我们使用 <> 来条件执行赋值操作,而无需使用异常: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_7,indent=0] +---- + +[TIP] +==== +如果值的种类是静态已知的,则可以通过解引用指针而不检查来获取对底层表示形式的引用。这避免了使用例如 <> 时可能出现的异常所带来的代码开销: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_8,indent=0] +---- +==== + +返回 {ref_result} 的函数允许您同时使用上述两种方式: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_9,indent=0] +---- + +=== 格式化输出 +当将 <> 输出到 {std_ostream} 时,其结果等同于调用 <> 所生成的序列化 JSON。 diff --git a/doc/pages/examples_zh_Hans.adoc b/doc/pages/examples_zh_Hans.adoc new file mode 100644 index 0000000..6ed6b88 --- /dev/null +++ b/doc/pages/examples_zh_Hans.adoc @@ -0,0 +1,41 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1] += 示例 + +== 美化 + +[source] +---- +include::../../example/pretty.cpp[tag=example_pretty,indent=0] +---- + +[#examples_validate] +== 验证 + +[source] +---- +include::../../example/validate.cpp[tag=example_validate,indent=0] +---- + +== 感知分配器的转换 + +[source] +---- +include::../../example/use_allocator.cpp[tag=example_use_allocator,indent=0] +---- + +== CBOR + +[source] +---- +include::../../example/cbor.cpp[tag=example_cbor,indent=0] +---- diff --git a/doc/pages/faq_zh_Hans.adoc b/doc/pages/faq_zh_Hans.adoc new file mode 100644 index 0000000..135d40c --- /dev/null +++ b/doc/pages/faq_zh_Hans.adoc @@ -0,0 +1,21 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 常见问题 + +"难道 simdjson 不是更快吗?":: 这些库不可相提并论。simdjson 解析器的输出是一个只读结构。换句话说,它不能被修改,创建它的唯一方法是解析一个 JSON 字符串。另一方面,Boost.JSON 允许你修改容纳已解析 JSON 的容器,甚至可以通过容器接口从头构建 JSON 文档。 + +"为什么不使用标准的 {req_Allocator}?":: 使用标准分配器将要求 <> 被声明为一个类模板,这会增加额外的编译负担。通过避免使用模板,库中的大部分函数定义可以从头文件中排除,并放入单独的静态库或动态库中。 + +"为什么使用 <> 而不是 {ref_polymorphic_allocator}?":: +{ref_polymorphic_allocator} 将内存资源视为所有权的引用。Boost.JSON 使用一个引用计数智能指针容器来简化内存资源的生命周期管理。除了引用计数外,<> 还可以作为围绕 {ref_memory_resource} 的非计数引用包装器。 + + +"为什么使用 <> 而不是 {std_string}?":: 该库提供的字符串使用 <>分配器模型,在所有 C++ 版本上具有相同的接口,并具有优化的类布局以保持 JSON 值的大小较小。<> 还实现了一个改进的接口,该接口使用 <> 替代了多余的重载。 diff --git a/doc/pages/io/overview_zh_Hans.adoc b/doc/pages/io/overview_zh_Hans.adoc new file mode 100644 index 0000000..daadac2 --- /dev/null +++ b/doc/pages/io/overview_zh_Hans.adoc @@ -0,0 +1,20 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#input_output, pagelevels=2] += 输入/输出 +该库提供解析和序列化算法,可根据需要在 JSON 与 <> 容器之间相互转换。这是通过自由函数和类实现的,具体如下所述。 + +:leveloffset: +1 + +include::parsing.adoc[] +include::serializing.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/io/parsing_zh_Hans.adoc b/doc/pages/io/parsing_zh_Hans.adoc new file mode 100644 index 0000000..5123a96 --- /dev/null +++ b/doc/pages/io/parsing_zh_Hans.adoc @@ -0,0 +1,181 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1] += 解析 +解析是将序列化的JSON文本验证并分解为元素的过程。该库提供以下函数和类型来辅助解析: + +.解析函数和类型 +|=== +|Name|描述 + +| <> +| 一个 SAX 推送式解析器实现,它将序列化的 JSON 文本转换为对用户提供的处理器的一系列成员函数调用,从而允许自定义内存中文档的表示行为。 + +| <> +| 一种结构,用于选择在解析期间启用哪些扩展。 + +| <> +| 解析包含完整序列化 JSON 文本的字符串,并返回一个 <>。 + +| <> +| 一种有状态的 DOM 解析器对象,可用于高效解析一系列 JSON 文本(每个文本均位于单独的连续字符缓冲区中),并将每个结果作为<>返回。 + +| <> +| 一种有状态的DOM解析器对象,可用于高效地增量解析一系列JSON文本,并将每个结果作为<>返回。 + +| <> +| 一种用于高效构建 <> 的低层构建块。解析器在内部使用它,用户也可以使用它来适配外部解析器以生成此库的容器。 +|=== + +<> 函数提供了一个简单接口,用于在单个函数调用中将序列化的JSON文本转换为<>。此重载使用异常来指示错误: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_1,indent=0] +---- + +或者,可以使用{ref_error_code}: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_2,indent=0] +---- + +即使使用错误码,底层 {ref_memory_resource} 仍可能抛出异常: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_3,indent=0] +---- + +前述示例中返回的 <> 使用了 <>(默认内存资源)。以下代码使用了 <>,从而实现更快的解析。`jv` 被标记为 `const` 以防止后续修改,因为使用单调内存资源的容器在被修改时会浪费内存。 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_4,indent=0] +---- + +== 非标准JSON +除非另有说明,本库中的解析器采用严格模式,仅识别有效的标准 JSON。通过填充一个 <> 结构并按值传递它来配置解析器,以允许某些非标准扩展。默认情况下,所有扩展都被禁用: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_5,indent=0] +---- + +当使用 {cpp}20 或更高版本构建时,可以对 <> 使用 https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers[指定初始化器]: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_6,indent=0] +---- + +启用 allow_invalid_utf16 后,解析器在遇到非法的前导代理、尾随代理或半个代理时不会抛出错误,而是将无效的 UTF-16 码点替换为 Unicode 替换字符。 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_15,indent=0] +---- + +CAUTION: 启用注释支持时,读取输入时需格外注意不要丢失空白字符。例如,`std::getline`会从它生成的字符串中移除行尾字符。 + +== 全精度数字解析 +库默认使用的数字解析算法速度较快,但可能导致轻微的精度损失。这在某些应用中可能不可接受,因此可以选择启用一种没有此缺陷但速度稍慢的替代算法。为此,您还需要使用<>结构体。 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_precise,indent=0] +---- + +请注意,全精度数字解析要求算法看到完整的数字。这意味着,当与<>一起使用时,可能需要额外的内存分配来存储解析器迄今为止接受的数字部分。该库尽力避免此类分配。 + +== 解析器 +<> 和 <> 的实例所提供的功能超出了使用 <> 自由函数时可用的功能: + +* 对内存的更精细控制 +* 流式 API,可增量解析输入的 JSON +* 在解析多个JSON文本时性能更佳 +* 忽略JSON文本末尾之后的非JSON内容 + +解析器实现使用临时存储空间在解析过程中累积值。使用 <> 自由函数时,该存储在每次调用中都会被分配和释放。然而,通过声明一个 <> 或 <> 实例,可在解析多个 JSON 文本时重用该临时存储,从而减少动态内存分配的总次数。 + +要使用 <>,先声明一个实例。然后调用 <> 一次,传入包含输入 JSON 的缓冲区。最后,在成功时调用 <> 以获取生成的 <> 的所有权。以下示例将解析器实例作为类成员保存,以便在多次调用间复用: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_7,indent=0] +---- + +有时,某个协议可能在 JSON 文本之后跟随采用不同格式或规范的数据。此时仍可使用函数 <> 来解析 JSON 部分。成功时,其返回值将指示从输入中消耗的字符数,该数量不包括非 JSON 的字符: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_8,indent=0] +---- + +解析器实例可以使用解析选项构造,这些选项允许识别一些非标准JSON扩展: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_9,indent=0] +---- + +== 流式解析器 +<>实现了一种https://en.wikipedia.org/wiki/Online_algorithm[__流式算法__];它允许使用一个或多个连续字符缓冲区增量处理大型JSON输入。整个输入JSON无需一次性加载到内存中。网络服务器可以使用流式接口以固定大小的量处理传入的JSON,从而提供以下优势: + +* 每个 I/O 周期的 CPU 消耗是有上限的 +* 每个 I/O 周期的内存消耗是有上限的 +* 减少抖动、不公平性和延迟 +* 处理完整输入所需的总内存更少 + +要使用 <>,请先声明一个实例。然后对表示输入 JSON 的连续缓冲区零次或多次调用 <>。当没有更多缓冲区时,调用 <>。在成功调用`write`或`finish`后,若解析已完成,函数 <> 将返回`true`。 + +在以下示例中,JSON文本从标准输入逐行解析。此处使用错误码替代异常。函数<>用于指示输入结束: + +CAUTION: 如果启用了注释,此示例将失效,原因在于使用了 `std::getline`(参见 <> 章节中的警告)。 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_10,indent=0] +---- + +我们可以通过从行序列中提取_多个_JSON值来进一步复杂化此示例。 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_14,indent=0] +---- + +== 控制内存 +在默认构造之后,或在无参调用 <> 之后,成功解析操作所生成的 <> 将使用默认内存资源。若要使用不同的内存资源,可调用 `reset`并传入目标资源。以下示例使用了 <>,该资源针对解析进行了优化,但不适合后续修改 + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_11,indent=0] +---- + +为实现性能与内存效率,解析器使用一块临时存储区来保存中间结果。在解析多个 JSON 文本时,该存储区会被复用,从而减少内存分配的总次数,提升性能。构造解析器时,可指定用于此临时存储区分配的内存资源;若未指定,则使用默认内存资源。此外,解析器还可利用调用方提供的缓冲区作为临时存储,有助于避免对小型输入进行动态分配。以下示例为解析器提供了一个 4 KB 的临时缓冲区,并在需要时回退到默认内存资源: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_12,indent=0] +---- + +== 避免动态分配 +通过精心指定缓冲区和内存资源,可以在解析 JSON 时完全消除所有动态内存分配,前提是整个JSON文本在单个字符缓冲区中可用,如下所示: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_13,indent=0] +---- + +== 自定义解析器 +希望实现自定义解析策略的用户可以创建自己的处理器(handler),并与 <> 实例配合使用。该处理器需实现 SAX 事件接口所要求的函数签名。在 <> 示例中,我们定义了一个“空”解析器(null parser),它会丢弃所有解析结果,用于实现一个判断 JSON 文本是否有效的函数。 diff --git a/doc/pages/io/serializing_zh_Hans.adoc b/doc/pages/io/serializing_zh_Hans.adoc new file mode 100644 index 0000000..45cd9f8 --- /dev/null +++ b/doc/pages/io/serializing_zh_Hans.adoc @@ -0,0 +1,50 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += 序列化 + +序列化是指将内存中由<>表示的JSON文档转换为字符序列的过程。该库提供以下用于序列化的自由函数和类型: + +.序列化函数和类型 +|=== +|Name|描述 + +| <> +| 将 <>、<>、<> 或 <> 序列化到 {std_ostream}。 + +| <> +| 返回一个 {std_string},表示序列化后的 <>、<>、<> 或 <>。 + +| <> +| 一个有状态的对象,可用于高效地序列化一个或多个 <>、<>、<> 或 <> 的实例。 +|=== + +为便于调试和输出,库中的容器类型可通过流操作符写入标准输出流: + +[source] +---- +include::../../../test/doc_serializing.cpp[tag=doc_serializing_1,indent=0] +---- + +<> 函数将一个 <> 转换为 {std_string}: + +[source] +---- +include::../../../test/doc_serializing.cpp[tag=doc_serializing_2,indent=0] +---- + +在完整序列化一个 <> 效率低下甚至不可行的情况下,可使用 <> 对其进行逐步序列化。这样做有多种原因,例如避免缓冲整个输出,或确保每个周期执行固定量的工作。<> 的实例通过内部动态分配的结构维护输出状态,并提供接口,将序列化输出的连续缓冲区写入调用方提供的缓冲区中。以下示例展示了如何使用 <> 来实现 <>: + +[source] +---- +include::../../../include/boost/json/impl/serialize.ipp[tag=example_operator_lt_lt,indent=0] +---- + +与解析器类似,序列化器可通过调用 <> 重复使用。这会将对象重置为序列化新实例的状态,并保留先前分配的内存,从而在序列化多个变量时提升性能。 diff --git a/doc/pages/main_zh_Hans.adoc b/doc/pages/main_zh_Hans.adoc new file mode 100644 index 0000000..bbd16ef --- /dev/null +++ b/doc/pages/main_zh_Hans.adoc @@ -0,0 +1,39 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Boost.JSON +(C) 2019--2020 Vinnie Falco; (C) 2020 Krystian Stasiowski; (C) 2022 Dmitry Arkhipov +:idprefix: +:sectanchors: +:toclevels: 2 +:toc: left +:pagelevels: 2 +:source-highlighter: rouge +:source-language: c++ + +include::definitions.adoc[] + +include::../../README.adoc[] + +:leveloffset: +1 + +include::quick_look.adoc[] +include::dom/overview.adoc[] +include::allocators/overview.adoc[] +include::io/overview.adoc[] +include::conversion/overview.adoc[] +include::examples.adoc[] +include::faq.adoc[] +include::benchmarks.adoc[] +include::comparison.adoc[] +include::reference.adoc[] +include::../../CHANGELOG.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/quick_look_zh_Hans.adoc b/doc/pages/quick_look_zh_Hans.adoc new file mode 100644 index 0000000..acc8965 --- /dev/null +++ b/doc/pages/quick_look_zh_Hans.adoc @@ -0,0 +1,217 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1,toclevels=1] += 快速浏览 +这里我们通过示例代码重点介绍重要特性,以帮助理解接口风格。首先包含库头文件,该文件将所有符号引入作用域;或者,也可以包含单独的头文件以获取特定类型的声明: + +[source] +---- +#include +---- + +为链接程序,您需要链接已构建的库。或者,您可以使用仅需头文件(header-only)的配置,只需在任意__一个__新建或现有源文件中包含此头文件: + +[source] +---- +#include +---- + +[NOTE] +==== +文中所使用的示例代码和标识符均假定以下声明已生效: + +[source] +---- +#include +using namespace boost::json; +---- +==== + +[#quick_look_values] +== 值 +假设您要在容器中重建一下 JSON 对象: + +[source, json] +---- +{ + "pi": 3.141, + "happy": true, + "name": "Boost", + "nothing": null, + "answer": { + "everything": 42 + }, + "list": [1, 0, 2], + "object": { + "currency": "USD", + "value": 42.99 + } +} +---- + +在本库中,类型 <>、<> 和 <> 分别用于表示 JSON 数组、对象和字符串,而类型 <> 是一种特殊的变体(variant),可容纳任意 JSON 元素。以下示例首先构造一个空对象,然后插入上述元素: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_1] +---- + +尽管键是字符串,但对象的映射类型和数组的元素类型均为前述的 <> 类型,它可以保存任意 JSON 元素,如前文赋值所示。除了通过一系列函数调用构建 JSON 文档外,我们还可以使用初始化列表在一条语句中完成构建: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_2] +---- + +当 <>, <>, 或 <> 通过初始化列表进行赋值或构造时,新值的创建仅发生一次。这使得初始化列表在效率上与其他创建值的方式相当。本库中的类型均为一等类型,支持复制和移动构造及赋值: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_3] +---- + +[#quick_look_allocators] +== 分配器 +为支持自定义内存分配策略,这些容器均允许通过一个 <>(指向 {ref_memory_resource} 的智能指针)进行构造。其构造函数签名的参数顺序与使用 {req_Allocator} 参数的标准库等价类型一致。容器一旦构造完成,其内存资源便不可更改。以下代码创建了一个未执行任何动态分配的数组: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_4] +---- + +本库中的容器强制保持一个不变式:容器的每个元素都使用相同的内存资源: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_5] +---- + +当库类型用作 PMR 容器(即使用 {ref_polymorphic_allocator} 的容器)的元素类型时,内存资源将自动传播至该类型及其所有子元素: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_6] +---- + +截至目前,我们已展示如何通过内存资源指针(不转移所有权)构造值。此时调用方需确保资源生命周期覆盖容器的生命周期。有时您希望容器获取资源的共享所有权。这可通过 <> 实现: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_7] +---- + +计数内存资源在拥有其共享所有权的每个容器均被销毁之前,是不会被销毁的。 + +[#quick_look_parsing] +== 解析 +可使用自由函数将 JSON 一步解析至值容器。在以下代码片段中,解析错误通过抛出异常指示: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_8] +---- + +也支持错误码: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_9] +---- + +默认情况下,解析器采用严格模式,仅接受符合标准的 JSON。但可通过填充一个选项结构体来启用一个或多个扩展,从而放宽此行为。以下示例使用静态缓冲区并启用了两个非标准扩展: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_10] +---- + +本库中的解析器实现了一种 https://en.wikipedia.org/wiki/Online_algorithm[__streaming algorithm__](流式算法);它可逐段处理 JSON,无需从一开始就提供完整输入。解析器在工作时会使用临时内存分配。如果计划解析多个 JSON(例如在网络服务器中),复用同一个解析器实例可以重用该临时存储,从而提升性能。 + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_11] +---- + +通过合理使用适当的内存资源、解析器实例及缓冲区大小的计算上限,可在不进行__任何__动态内存分配的情况下解析和检查 JSON。后续章节将对此进行更详细探讨。 + +[#quick_look_serializing] +== 序列化 +提供了简单的自由函数,用于将 <> 序列化为包含 JSON 的 {std_string}: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_12] +---- + +本库中的序列化器实现了 https://en.wikipedia.org/wiki/Online_algorithm[__streaming algorithm__](流式算法);它可逐段输出 JSON,无需一次性分配整个输出空间: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_13] +---- + +[#quick_look_conversion] +== 值转换 +给定一个用户定义的类型: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_14] +---- + +我们可在同一命名空间中定义 `tag_invoke` 的重载,来实现从用户定义类型到 <> 的转换。此操作将 `customer` 映射为 JSON 对象: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_15] +---- + +这允许我们可以使用库函数 <> 从我们的类型生成一个 <>: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_16] +---- + +该库能够自动处理标准容器。以下我们将一个客户数组转换为一个值: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_17] +---- + +为将 JSON 转换为用户定义类型,我们使用 <>(它会调用 `tag_invoke` 的另一个重载)。此操作将 JSON 值转换为 `customer`。若值内容不符合预期,则抛出异常: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_18] +---- + +上述代码定义了便捷函数 `extract`,它能推导结构体成员的类型。这种方式可行,但要求该结构体是 {req_DefaultConstructible}。另一种方法是直接构造对象,虽然略显冗长,但无需默认构造: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_19] +---- + +现在我们可以从 JSON 构造客户对象(customer): + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_20] +---- + +当将 <> 转换为类似数组或对象的容器时,库的通用算法可自动识别。因此若要将 JSON 数组转换为客户向量,可编写如下代码: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_21] +---- diff --git a/doc/pages/reference_zh_Hans.adoc b/doc/pages/reference_zh_Hans.adoc new file mode 100644 index 0000000..d294480 --- /dev/null +++ b/doc/pages/reference_zh_Hans.adoc @@ -0,0 +1,98 @@ +//// +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#ref, pagelevels=3,toclevels=1] += 参考 + +[cols=4*a,autowidth] +|=== +4+|JSON + +a| *类* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +a| *函数* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +*P0308R0* + +<> + +<> + +<> + +| *运算符* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + + +*别名* + +<> + +<> + +*常量* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +| *类型特征* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +|=== + +include::{entities-file}[leveloffset=+1]