jsoncons 是一个 C++ 纯标头库,用于构建 JSON 和类似 JSON 的数据格式,例如 CBOR。对于每种支持的数据格式,它使您能够通过多种方式处理数据:
作为一个类似变体的、分配器感知的数据结构,basic_json
作为实现 json_type_traits 的强类型 C++ 数据结构
通过游标级访问解析事件流,有点类似于 XML 世界中的 StAX 拉解析和推送序列化。
与其他 JSON 库相比,jsoncons 被设计用于处理非常大的 JSON 文本。其核心是 SAX 风格的解析器和序列化器。它支持以类似变体的结构读取内存中的整个 JSON 文本。但它还支持使用 StAX 风格的拉式解析和推式序列化来高效访问底层数据。它支持使用 json_type_traits 专业化提供的有关用户类型的信息,增量解析为用户的首选形式。
jsoncons 数据模型支持熟悉的 JSON 类型 - 空值、布尔值、数字、字符串、数组、对象 - 以及字节字符串。此外,jsoncons 支持日期时间、纪元时间、大整数、大小数、大浮点数和二进制编码的语义标记。这使得它能够在解析类似 JSON 的数据格式(例如具有这些类型语义的 CBOR)时保留这些类型语义。
jsoncons 根据 Boost 软件许可证分发。
jsoncons 是免费的,但欢迎支持以维持其发展。如果您发现这个图书馆有帮助,请考虑一次性捐赠或成为❤️赞助商。
随着jsoncons
库的发展,名称有时会发生变化。为了简化过渡,jsoncons 弃用了旧名称,但继续支持其中的许多名称。可以通过定义宏JSONCONS_NO_DEPRECATED
来抑制已弃用的名称,建议新代码这样做。
“Apache Kvrocks 始终利用 jsoncons 为用户提供对 JSON 数据结构的支持。我们发现 jsoncons 的开发体验非常出色!”
“我一直在用我的母语 R 来使用你们的库,并创建了一个 R 包,以便 (a) 对 JSON 字符串或 R 对象进行 JMESpath 和 JSONpath 查询,以及 (b) 其他 R 开发人员链接到您的库”。
“我使用您的库作为外部接口来传递数据,以及使用从 csv 到 json 的转换,这对于转换数据以在 javascript 中使用非常有帮助”
“已验证,根据我对 JSON 和 CBOR 的需求,它运行良好”
“这个库的 JSONPath 功能,非常棒”
“我们相当广泛地使用 JMESPath 实现”
“我们喜欢您的 JSON 模式验证器。我们在 ER/Studio 数据建模工具中使用它来解析 JSON 模式文件,以便我们可以从中创建实体关系模型。”
“选择的序列化库具有漂亮的映射和易用性”
“真的很好” “很棒的项目” “非常坚固且非常可靠” “我的团队喜欢它” “你的仓库很棒!!!!!”
开始使用 AWS 开发工具包使用 HealthImaging 图像集和图像帧
RubyGems.org rjsoncons Coppelia Robotics CSPro 许可证
您可以使用 vcpkg 平台库管理器来安装 jsoncons 包。
或者,下载最新版本并解压 zip 文件。将目录include/jsoncons
复制到您的include
目录。如果您想使用扩展,请同时复制include/jsoncons_ext
。
或者,在 main.c 上下载最新的代码。
该库需要支持 C++11 的 C++ 编译器。此外,该库定义了jsoncons::endian
、 jsoncons::basic_string_view
、 jsoncons::optional
和jsoncons::span
,如果检测到它们,它们将被类型定义为其标准库等效项。否则,它们将被类型定义为内部的、C++11 兼容的实现。
该库使用异常,在某些情况下使用 std::error_code 来报告错误。除了jsoncons::assertion_error
之外,所有 jsoncons 异常类都实现 jsoncons::json_error 接口。如果禁用异常或者定义了编译时宏JSONCONS_NO_EXCEPTIONS
,则抛出将变为对std::terminate
调用。
json_benchmarks 提供了一些有关jsoncons
与其他json
库比较的测量结果。
JSONTestSuite 和 JSON_checker 测试套件
文本和整数的性能基准
文本和双打的性能基准
JSONPath Comparison 显示 jsoncons JsonPath 与其他实现的比较
处理 JSON 数据
使用 CBOR 数据
对于下面的示例,您需要包含一些头文件并初始化一串 JSON 数据:
# include < jsoncons/json.hpp >
# include < jsoncons_ext/jsonpath/jsonpath.hpp >
# include < iostream >
using namespace jsoncons ; // for convenience
std::string data = R"(
{
"application": "hiking",
"reputons": [
{
"rater": "HikingAsylum",
"assertion": "advanced",
"rated": "Marilyn C",
"rating": 0.90,
"generated": 1514862245
}
]
}
)" ;
jsoncons 允许您以多种方式处理数据:
basic_json 作为一种类似变体的数据结构
作为实现 json_type_traits 的强类型 C++ 数据结构
通过游标级访问解析事件流
int main ()
{
// Parse the string of data into a json value
json j = json::parse (data);
// Does object member reputons exist?
std::cout << " (1) " << std::boolalpha << j. contains ( " reputons " ) << " nn " ;
// Get a reference to reputons array
const json& v = j[ " reputons " ];
// Iterate over reputons array
std::cout << " (2) n " ;
for ( const auto & item : v. array_range ())
{
// Access rated as string and rating as double
std::cout << item[ " rated " ]. as <std::string>() << " , " << item[ " rating " ]. as < double >() << " n " ;
}
std::cout << " n " ;
// Select all "rated" with JSONPath
std::cout << " (3) n " ;
json result = jsonpath::json_query (j, " $..rated " );
std::cout << pretty_print (result) << " nn " ;
// Serialize back to JSON
std::cout << " (4) n " << pretty_print (j) << " nn " ;
}
输出:
(1) true
(2)
Marilyn C, 0.9
(3)
[
"Marilyn C"
]
(4)
{
"application": "hiking",
"reputons": [
{
"assertion": "advanced",
"generated": 1514862245,
"rated": "Marilyn C",
"rater": "HikingAsylum",
"rating": 0.9
}
]
}
jsoncons 支持将 JSON 文本转换为 C++ 数据结构。函数decode_json 和encode_json 将字符串或JSON 数据流转换为C++ 数据结构并返回。对定义了 json_type_traits 的所有 C++ 类进行解码和编码工作。 jsoncons 已经支持标准库中的许多类型,如果您在jsoncons
命名空间中专门化json_type_traits
,您自己的类型也将受到支持。
namespace ns {
enum class hiking_experience {beginner,intermediate,advanced};
class hiking_reputon
{
std::string rater_;
hiking_experience assertion_;
std::string rated_;
double rating_;
std::optional<std::chrono::seconds> generated_; // assumes C++17, if not use jsoncons::optional
std::optional<std::chrono::seconds> expires_;
public:
hiking_reputon ( const std::string& rater,
hiking_experience assertion,
const std::string& rated,
double rating,
const std::optional<std::chrono::seconds>& generated =
std::optional<std::chrono::seconds>(),
const std::optional<std::chrono::seconds>& expires =
std::optional<std::chrono::seconds>())
: rater_(rater), assertion_(assertion), rated_(rated), rating_(rating),
generated_ (generated), expires_(expires)
{
}
const std::string& rater () const { return rater_;}
hiking_experience assertion () const { return assertion_;}
const std::string& rated () const { return rated_;}
double rating () const { return rating_;}
std::optional<std::chrono::seconds> generated () const { return generated_;}
std::optional<std::chrono::seconds> expires () const { return expires_;}
friend bool operator ==( const hiking_reputon& lhs, const hiking_reputon& rhs)
{
return lhs. rater_ == rhs. rater_ && lhs. assertion_ == rhs. assertion_ &&
lhs. rated_ == rhs. rated_ && lhs. rating_ == rhs. rating_ &&
lhs. confidence_ == rhs. confidence_ && lhs. expires_ == rhs. expires_ ;
}
friend bool operator !=( const hiking_reputon& lhs, const hiking_reputon& rhs)
{
return !(lhs == rhs);
};
};
class hiking_reputation
{
std::string application_;
std::vector<hiking_reputon> reputons_;
public:
hiking_reputation ( const std::string& application,
const std::vector<hiking_reputon>& reputons)
: application_(application),
reputons_ (reputons)
{}
const std::string& application () const { return application_;}
const std::vector<hiking_reputon>& reputons () const { return reputons_;}
};
} // namespace ns
// Declare the traits. Specify which data members need to be serialized.
JSONCONS_ENUM_TRAITS (ns::hiking_experience, beginner, intermediate, advanced)
// First four members listed are mandatory, generated and expires are optional
JSONCONS_N_CTOR_GETTER_TRAITS(ns::hiking_reputon, 4 , rater, assertion, rated, rating,
generated, expires)
// All members are mandatory
JSONCONS_ALL_CTOR_GETTER_TRAITS(ns::hiking_reputation, application, reputons)
int main()
{
// Decode the string of data into a c++ structure
ns::hiking_reputation v = decode_json<ns::hiking_reputation>(data);
// Iterate over reputons array value
std::cout << " (1) n " ;
for ( const auto & item : v. reputons ())
{
std::cout << item. rated () << " , " << item. rating ();
if (item. generated ())
{
std::cout << " , " << (*item. generated ()). count ();
}
std::cout << " n " ;
}
// Encode the c++ structure into a string
std::string s;
encode_json (v, s, indenting::indent);
std::cout << " (2) n " ;
std::cout << s << " n " ;
}
输出:
(1)
Marilyn C, 0.9, 1514862245
(2)
{
"application": "hiking",
"reputons": [
{
"assertion": "advanced",
"generated": 1514862245,
"rated": "Marilyn C",
"rater": "HikingAsylum",
"rating": 0.9
}
]
}
此示例使用便捷宏JSONCONS_ENUM_TRAITS
、 JSONCONS_N_CTOR_GETTER_TRAITS
和JSONCONS_ALL_CTOR_GETTER_TRAITS
来专门化枚举类型ns::hiking_experience
、类ns::hiking_reputon
(带有一些非强制成员)和类的 json_type_traits ns::hiking_reputation
(包含所有强制成员)。宏JSONCONS_ENUM_TRAITS
从枚举标识符生成代码,宏JSONCONS_N_CTOR_GETTER_TRAITS
和JSONCONS_ALL_CTOR_GETTER_TRAITS
从 get 函数和构造函数生成代码。这些宏声明必须放置在任何命名空间块之外。
请参阅示例以了解专门化json_type_traits
的其他方法。
典型的拉式解析应用程序将重复处理current()
事件并调用next()
前进到下一个事件,直到done()
返回true
。
int main ()
{
json_string_cursor cursor (data);
for (; !cursor. done (); cursor. next ())
{
const auto & event = cursor. current ();
switch (event. event_type ())
{
case staj_event_type::begin_array:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::end_array:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::begin_object:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::end_object:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::key:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
case staj_event_type::null_value:
std::cout << event. event_type () << " n " ;
break ;
case staj_event_type::bool_value:
std::cout << event. event_type () << " : " << std::boolalpha << event. get < bool >() << " n " ;
break ;
case staj_event_type::int64_value:
std::cout << event. event_type () << " : " << event. get < int64_t >() << " n " ;
break ;
case staj_event_type::uint64_value:
std::cout << event. event_type () << " : " << event. get < uint64_t >() << " n " ;
break ;
case staj_event_type::double_value:
std::cout << event. event_type () << " : " << event. get < double >() << " n " ;
break ;
default :
std::cout << " Unhandled event type: " << event. event_type () << " " << " n " ;
break ;
}
}
}
输出:
begin_object
key: application
string_value: hiking
key: reputons
begin_array
begin_object
key: rater
string_value: HikingAsylum
key: assertion
string_value: advanced
key: rated
string_value: Marilyn C
key: rating
double_value: 0.9
key: generated
uint64_value: 1514862245
end_object
end_array
end_object
您可以使用管道语法将过滤器应用于游标(例如, cursor | filter1 | filter2 | ...
)
int main ()
{
std::string name;
auto filter = [&]( const staj_event& ev, const ser_context&) -> bool
{
if (ev. event_type () == staj_event_type::key)
{
name = ev. get <std::string>();
return false ;
}
if (name == " rated " )
{
name. clear ();
return true ;
}
return false ;
};
json_string_cursor cursor (data);
auto filtered_c = cursor | filter;
for (; !filtered_c. done (); filtered_c. next ())
{
const auto & event = filtered_c. current ();
switch (event. event_type ())
{
case staj_event_type::string_value:
// Or std::string_view, if C++17
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
default :
std::cout << " Unhandled event type n " ;
break ;
}
}
}
输出:
Marilyn C
对于下面的示例,您需要包含一些头文件并初始化 CBOR 数据的缓冲区:
# include < iomanip >
# include < iostream >
# include < jsoncons/json.hpp >
# include < jsoncons_ext/cbor/cbor.hpp >
# include < jsoncons_ext/jsonpath/jsonpath.hpp >
using namespace jsoncons ; // for convenience
const std::vector< uint8_t > data = {
0x9f , // Start indefinte length array
0x83 , // Array of length 3
0x63 , // String value of length 3
0x66 , 0x6f , 0x6f , // "foo"
0x44 , // Byte string value of length 4
0x50 , 0x75 , 0x73 , 0x73 , // 'P''u''s''s'
0xc5 , // Tag 5 (bigfloat)
0x82 , // Array of length 2
0x20 , // -1
0x03 , // 3
0x83 , // Another array of length 3
0x63 , // String value of length 3
0x62 , 0x61 , 0x72 , // "bar"
0xd6 , // Expected conversion to base64
0x44 , // Byte string value of length 4
0x50 , 0x75 , 0x73 , 0x73 , // 'P''u''s''s'
0xc4 , // Tag 4 (decimal fraction)
0x82 , // Array of length 2
0x38 , // Negative integer of length 1
0x1c , // -29
0xc2 , // Tag 2 (positive bignum)
0x4d , // Byte string value of length 13
0x01 , 0x8e , 0xe9 , 0x0f , 0xf6 , 0xc3 , 0x73 , 0xe0 , 0xee , 0x4e , 0x3f , 0x0a , 0xd2 ,
0xff // "break"
};
jsoncons 允许您像 JSON 数据一样使用 CBOR 数据:
basic_json 作为一种类似变体的数据结构
作为实现 json_type_traits 的强类型 C++ 数据结构
通过游标级访问解析事件流
int main ()
{
// Parse the CBOR data into a json value
json j = cbor::decode_cbor<json>(data);
// Pretty print
std::cout << " (1) n " << pretty_print (j) << " nn " ;
// Iterate over rows
std::cout << " (2) n " ;
for ( const auto & row : j. array_range ())
{
std::cout << row[ 1 ]. as <jsoncons::byte_string>() << " ( " << row[ 1 ]. tag () << " ) n " ;
}
std::cout << " n " ;
// Select the third column with JSONPath
std::cout << " (3) n " ;
json result = jsonpath::json_query (j, " $[*][2] " );
std::cout << pretty_print (result) << " nn " ;
// Serialize back to CBOR
std::vector< uint8_t > buffer;
cbor::encode_cbor (j, buffer);
std::cout << " (4) n " << byte_string_view (buffer) << " nn " ;
}
输出:
(1)
[
["foo", "UHVzcw", "0x3p-1"],
["bar", "UHVzcw==", "1.23456789012345678901234567890"]
]
(2)
50,75,73,73 (n/a)
50,75,73,73 (base64)
(3)
[
"0x3p-1",
"1.23456789012345678901234567890"
]
(4)
82,83,63,66,6f,6f,44,50,75,73,73,c5,82,20,03,83,63,62,61,72,d6,44,50,75,73,73,c4,82,38,1c,c2,4d,01,8e,e9,0f,f6,c3,73,e0,ee,4e,3f,0a,d2
int main ()
{
// Parse the string of data into a std::vector<std::tuple<std::string,jsoncons::byte_string,std::string>> value
auto val = cbor::decode_cbor<std::vector<std::tuple<std::string,jsoncons::byte_string,std::string>>>(data);
std::cout << " (1) n " ;
for ( const auto & row : val)
{
std::cout << std::get< 0 >(row) << " , " << std::get< 1 >(row) << " , " << std::get< 2 >(row) << " n " ;
}
std::cout << " n " ;
// Serialize back to CBOR
std::vector< uint8_t > buffer;
cbor::encode_cbor (val, buffer);
std::cout << " (2) n " << byte_string_view (buffer) << " nn " ;
}
输出:
(1)
foo, 50,75,73,73, 0x3p-1
bar, 50,75,73,73, 1.23456789012345678901234567890
(2)
82,9f,63,66,6f,6f,44,50,75,73,73,66,30,78,33,70,2d,31,ff,9f,63,62,61,72,44,50,75,73,73,78,1f,31,2e,32,33,34,35,36,37,38,39,30,31,32,33,34,35,36,37,38,39,30,31,32,33,34,35,36,37,38,39,30,ff
请注意,当将 bigfloat 和小数部分解码为std::string
时,我们会丢失变量(例如使用标签保存的数据结构)的语义信息,因此序列化回 CBOR 会生成文本字符串。
典型的拉式解析应用程序将重复处理current()
事件并调用next()
前进到下一个事件,直到done()
返回true
。
int main ()
{
cbor::cbor_bytes_cursor cursor (data);
for (; !cursor. done (); cursor. next ())
{
const auto & event = cursor. current ();
switch (event. event_type ())
{
case staj_event_type::begin_array:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::end_array:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::begin_object:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::end_object:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::key:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::byte_string_value:
std::cout << event. event_type () << " : " << event. get <jsoncons::span< const uint8_t >>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::null_value:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::bool_value:
std::cout << event. event_type () << " : " << std::boolalpha << event. get < bool >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::int64_value:
std::cout << event. event_type () << " : " << event. get < int64_t >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::uint64_value:
std::cout << event. event_type () << " : " << event. get < uint64_t >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::half_value:
case staj_event_type::double_value:
std::cout << event. event_type () << " : " << event. get < double >() << " " << " ( " << event. tag () << " ) n " ;
break ;
default :
std::cout << " Unhandled event type " << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
}
}
}
输出:
begin_array (n/a)
begin_array (n/a)
string_value: foo (n/a)
byte_string_value: 50,75,73,73 (n/a)
string_value: 0x3p-1 (bigfloat)
end_array (n/a)
begin_array (n/a)
string_value: bar (n/a)
byte_string_value: 50,75,73,73 (base64)
string_value: 1.23456789012345678901234567890 (bigdec)
end_array (n/a)
end_array (n/a)
您可以使用管道语法将过滤器应用于游标,
int main ()
{
auto filter = [&]( const staj_event& ev, const ser_context&) -> bool
{
return (ev. tag () == semantic_tag::bigdec) || (ev. tag () == semantic_tag::bigfloat);
};
cbor::cbor_bytes_cursor cursor (data);
auto filtered_c = cursor | filter;
for (; !filtered_c. done (); filtered_c. next ())
{
const auto & event = filtered_c. current ();
switch (event. event_type ())
{
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
default :
std::cout << " Unhandled event type " << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
}
}
}
输出:
string_value: 0x3p-1 (bigfloat)
string_value: 1.23456789012345678901234567890 (bigdec)
jsoncons 需要一个至少支持 C++11 的编译器。它在 Github Actions 和 Circleci 上进行了持续集成测试。为选定的 gcc 和 clang 版本启用 UndefinedBehaviorSanitizer (UBSan) 诊断。自 v0.151.0 起,它与 Google OSS-fuzz 集成,覆盖所有解析器和编码器。
编译器 | 版本 | 标准 | 建筑学 | 操作系统 | 持续集成服务 |
---|---|---|---|---|---|
视觉工作室 | vs2019 | 默认 | x86、x64 | 视窗11 | GitHub 操作 |
vs2022 | 默认 | x86、x64 | 视窗11 | GitHub 操作 | |
Visual Studio - 铿锵声 | vs2019 | 默认 | x86、x64 | 视窗11 | GitHub 操作 |
vs2022 | 默认 | x86、x64 | 视窗11 | GitHub 操作 | |
克++ | 6、7、8、9、10、11、12 | 默认 | x64 | 乌班图 | 圈子 |
克++ | 12 | c++20 | x64 | 乌班图 | GitHub 操作 |
铛 | 3.9、4、5、6、7、8、9、10、11、12、13、14、15 | 默认 | x64 | 乌班图 | 圈子 |
铛 | 14 | c++20 | x64 | 乌班图 | GitHub 操作 |
铿锵xcode | 11, 12, 13 | 默认 | x64 | 操作系统X 11 | GitHub 操作 |
铿锵xcode | 13, 14 | 默认 | x64 | 操作系统X 12 | GitHub 操作 |
CMake 是一个跨平台构建工具,可以为您选择的编译器环境生成 makefile 和解决方案。在 Windows 上,您可以下载 Windows 安装程序包。在 Linux 上,它通常作为软件包提供,例如,在 Ubuntu 上,
sudo apt-get install cmake
安装 cmake 后,您可以从 jsoncons 目录构建并运行单元测试,
在 Windows 上:
> mkdir build
> cd build
> cmake .. -DJSONCONS_BUILD_TESTS=On
> cmake --build .
> ctest -C Debug --output-on-failure
在 UNIX 上:
$ mkdir build
$ cd build
$ cmake .. -DJSONCONS_BUILD_TESTS=On
$ cmake --build .
$ ctest --output-on-failure
jsoncons 使用 PVS-Studio 静态分析器,为开源项目免费提供。
非常感谢 comp.lang.c++ 社区在实现细节方面提供的帮助。
jsoncons 平台相关的二进制配置借鉴了优秀的 MIT 许可的tinycbor。
感谢 RapidJSON 的作者 Milo Yip,通过发布基准测试并联系该项目(以及其他项目)来分享结果,全面提高了 JSON 库的质量。
用于打印浮点数的 Grisu3 算法的 jsoncons 实现遵循 Florian Loitsch 的 MIT 许可的 grisu3_59_56 实现,并进行了少量修改。
宏JSONCONS_ALL_MEMBER_TRAITS
遵循 Martin York 的 ThorsSerializer 所采用的方法
BSON Decimal128 与字符串之间的 jsoncons 实现以及 ObjectId 与字符串之间的实现基于 Apache 2 许可的 libbson。
特别感谢我们的贡献者