跳到主要内容

Boost.JSON

综述

Boost.JSON是一个可移植的C++库,提供了实现JavaScript Object Notation或简称为JSON(一种轻量级数据交换格式)的容器和算法。这种格式对人类来说很容易读写,对机器来说也很容易解析和生成。它基于JavaScript编程语言(Standard ECMA-262)的子集。 JSON是一种文本格式,与语言无关,但是使用C语言家族(包括C,C ++,C#,Java,JavaScript,Perl,Python等)的程序员熟悉的约定。这些属性使JSON成为理想的数据交换语言。

该库着重于一个常见且流行的用例:在一个名为value的容器中进行解析和序列化,该容器保存JSON类型。您生成的任何值都可以序列化然后反序列化,以确保结果将等于原始值。无论使用此库生成的任何JSON输出,大多数常见的JSON实现都可以用任何语言读取。

value容器的设计使其非常适合作为词汇类型,适合在公共接口和库中使用,从而可以构成它们。该库将可表示的数据类型限制为大多数JSON实现(尤其是JavaScript)几乎普遍接受的范围。解析器和序列化都具有很高的性能,可达到或超过最佳可比库的基准性能。分配器得到很好的支持。使用这些类型的代码将易于理解,灵活且高效。

Boost.JSON提供以下功能:

  • 快速编译
  • 只需要C ++ 11
  • 快速流解析器和序列化
  • 常量时间复杂度的对象键查找
  • 允许非标准JSON的选项
  • 带有分配器支持的简单安全的现代API
  • 不进行Boost编译,定义BOOST_JSON_STANDALONE
  • 仅有头文件,无需链接到库

要求

支持的编译器

快速浏览

在这里,我们通过示例代码突出显示了重要的功能,以帮助传达接口的样式。 我们从包含库头文件开始,该文件将所有符号带入范围。 或者,可以包含各个头文件以获得特定类型的声明:

#include <boost/json.hpp>

为了链接您的程序,您将需要与内置库链接。 另外,您可以通过仅在所有新的或现有的源文件中包含此头文件来使用仅头文件配置:

#include <boost/json/src.hpp>

贯穿全文使用的示例代码和标识符的编写方式类似于以下声明:

#include <boost/json.hpp>
using namespace boost::json;

假设您要在容器中重新创建此JSON对象:

{
"pi": 3.141,
"happy": true,
"name": "Boost",
"nothing": null,
"answer": {
"everything": 42
},
"list": [1, 0, 2],
"object": {
"currency": "USD",
"value": 42.99
}
}

在此库中,数组,对象和字符串分别保存为JSON数组,对象和字符串,而类型value是一个特殊的变体,可以容纳任何JSON元素。 在这里,我们构造一个空对象,然后插入上面的元素:

object obj;                                                     // construct an empty object
obj[ "pi" ] = 3.141; // insert a double
obj[ "happy" ] = true; // insert a bool
obj[ "name" ] = "Boost"; // insert a string
obj[ "nothing" ] = nullptr; // insert a null
obj[ "answer" ].emplace_object()["everything"] = 42; // insert an object with 1 element
obj[ "list" ] = { 1, 0, 2 }; // insert an array with 3 elements
obj[ "object" ] = { {"currency", "USD"}, {"value", 42.99} }; // insert an object with 2 elements

虽然键是字符串,但是对象的映射类型和数组的元素类型是一种称为value的特殊类型,可以容纳任何JSON元素,如先前的分配所示。 无需使用一系列函数调用来构建JSON文档,我们可以使用初始化列表在一个语句中构建它:

value jv = {
{ "pi", 3.141 },
{ "happy", true },
{ "name", "Boost" },
{ "nothing", nullptr },
{ "answer", {
{ "everything", 42 } } },
{"list", {1, 0, 2}},
{"object", {
{ "currency", "USD" },
{ "value", 42.99 }
} }
};

当从初始化列表中分配或构造值,数组或对象时,新值的创建仅发生一次。 这使初始化程序列表与使用其他方式创建值的效率相同。 该库中的类型是一等值(first-class),支持复制和移动构造以及分配:

array arr;                                          // construct an empty array
arr = { 1, 2, 3 }; // replace the contents with 3 elements
value jv1( arr ); // this makes a copy of the array
value jv2( std::move(arr) ); // this performs a move-construction

assert( arr.empty() ); // moved-from arrays become empty
arr = { nullptr, true, "boost" }; // fill in the array again

Allocators

为了允许自定义内存分配策略,这些容器都允许使用storage_ptr进行构造,该存储指针是指向memory_resource的智能指针。 构造函数签名与其使用Allocator参数的std对等符具有相同的顺序。 一旦构造了容器,其内存资源就永远不会改变。 在这里,我们创建一个数组而不执行任何动态分配:

{
unsigned char buf[ 4096 ]; // storage for our array
static_resource mr( buf ); // memory resource which uses buf
array arr( &mr ); // construct using the memory resource
arr = { 1, 2, 3 }; // all allocated memory comes from `buf`
}

此库中的容器强制不变,即容器的每个元素都将使用相同的内存资源:

{
monotonic_resource mr; // memory resource optimized for insertion
array arr( &mr ); // construct using the memory resource
arr.resize( 1 ); // make space for one element
arr[ 0 ] = { 1, 2, 3 }; // assign an array to element 0
assert( *arr[0].storage() == *arr.storage() ); // same memory resource
}

当库类型用作pmr容器的元素类型时; 也就是说,使用polymorphic_allocator的容器,内存资源将自动传播到该类型及其所有子代:

{
monotonic_resource mr;
boost::container::pmr::vector< value > vv( &mr );
vv.resize( 3 );

// The memory resource of the container is propagated to each element
assert( *vv.get_allocator().resource() == *vv[0].storage() );
assert( *vv.get_allocator().resource() == *vv[1].storage() );
assert( *vv.get_allocator().resource() == *vv[2].storage() );
}

文件模型

数组

目的

号码

价值转换

初始化列表

分配者

背景

storage_ptr

使用分配器构造

输入/输出

该库提供了解析和序列化算法,可根据需要在value容器之间来回转换JSON。 这是通过函数和类完成的,如下所述。

解析

解析是验证序列化JSON并将其分解为元素的过程。 该库提供以下功能和类型以帮助解析:

parse函数提供了一个简单的接口,用于在单个函数调用中将序列化的JSON文本转换为value。 此重载使用异常来指示错误:

value jv = parse( "[1,2,3,4,5]" );

或者,可以使用error_code

error_code ec;
value jv = parse( "[1,2,3,4,5]", ec );
if( ec )
std::cout << "Parsing failed: " << ec.message() << "\n";

即使使用错误代码,从底层memory_resource引发的异常仍然可能:

try {
error_code ec;
value jv = parse( "[1,2,3,4,5]", ec );
if( ec )
std::cout << "Parsing failed: " << ec.message() << "\n";
} catch( std::bad_alloc const& e) {
std::cout << "Parsing failed: " << e.what() << "\n";
}

前面的示例中返回的value使用默认的内存资源。 以下代码使用monotonic_resource,这将加快解析速度。 将jv标记为const可以防止后续修改,因为当mutated时使用monotonic resource的容器会浪费内存。

避免动态分配

序列化

例子

漂亮

验证

经常问的问题

基准测试

与其他图书馆的比较

参考

指数