json
对象那里有无数的 JSON 库,每个库甚至都有其存在的理由。我们班有以下设计目标:
直观的语法。在 Python 等语言中,JSON 感觉像是一流的数据类型。我们使用了现代 C++ 的所有运算符魔法来在代码中实现相同的感觉。看看下面的例子,你就会明白我的意思。
琐碎的整合。我们的整个代码由一个头文件json.hpp
组成。就是这样。没有库,没有子项目,没有依赖项,没有复杂的构建系统。该类是用普通 C++11 编写的。总而言之,一切都不需要调整编译器标志或项目设置。
认真测试。我们的代码经过严格的单元测试,覆盖了 100% 的代码,包括所有异常行为。此外,我们还检查了 Valgrind 和 Clang Sanitizers 是否存在内存泄漏。 Google OSS-Fuzz 还对所有解析器 24/7 运行模糊测试,迄今为止有效执行了数十亿次测试。为了保持高质量,该项目遵循核心基础设施计划 (CII) 最佳实践。
其他方面对我们来说并不那么重要:
记忆效率。每个 JSON 对象都有一个指针(联合的最大大小)和一个枚举元素(1 个字节)的开销。默认泛化使用以下 C++ 数据类型: std::string
表示字符串, int64_t
、 uint64_t
或double
表示数字, std::map
表示对象, std::vector
表示数组, bool
表示布尔值。但是,您可以根据需要对通用类basic_json
进行模板化。
速度。当然有更快的 JSON 库。但是,如果您的目标是通过使用单个标头添加 JSON 支持来加快开发速度,那么这个库就是您的最佳选择。如果您知道如何使用std::vector
或std::map
,那么您已经准备好了。
有关更多信息,请参阅贡献指南。
您可以在 GitHub Sponsors 上赞助该库。
谢谢大家!
❓ 如果您有疑问,请检查常见问题解答或问答部分是否已得到解答。如果没有,请在那里提出一个新问题。
如果您想了解有关如何使用该库的更多信息,请查看自述文件的其余部分,查看代码示例,或浏览帮助页面。
?如果您想更好地了解API ,请查看API 参考。
?如果您发现错误,请查看常见问题解答(它是已知问题还是设计决策的结果)。在创建新问题之前,还请查看问题列表。请提供尽可能多的信息,以帮助我们理解和重现您的问题。
还有一个适用于文档浏览器 Dash、Velocity 和 Zeal的文档集,其中包含作为离线资源的完整文档。
以下是一些示例,可帮助您了解如何使用该类。
除了下面的示例之外,您可能还想:
→ 查看文档
→ 浏览独立示例文件
每个 API 函数(在 API 文档中记录)都有一个相应的独立示例文件。例如, emplace()
函数有一个匹配的 emplace.cpp 示例文件。
json
类提供了用于操作 JSON 值的 API。通过读取 JSON 文件来创建json
对象:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
对象假设您想要在文件中创建硬编码这个文本 JSON 值作为json
对象:
{
"pi" : 3.141 ,
"happy" : true
}
有多种选择:
// Using (raw) string literals and json::parse
json ex1 = json::parse( R"(
{
"pi": 3.141,
"happy": true
}
)" );
// Using user-defined (raw) string literals
using namespace nlohmann ::literals ;
json ex2 = R"(
{
"pi": 3.141,
"happy": true
}
)" _json;
// Using initializer lists
json ex3 = {
{ " happy " , true },
{ " pi " , 3.141 },
};
以下是一些示例,可帮助您了解如何使用该类。
假设您要创建 JSON 对象
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
有了这个库,你可以写:
// create an empty structure (null)
json j;
// add a number that is stored as double (note the implicit conversion of j to an object)
j[ " pi " ] = 3.141 ;
// add a Boolean that is stored as bool
j[ " happy " ] = true ;
// add a string that is stored as std::string
j[ " name " ] = " Niels " ;
// add another null object by passing nullptr
j[ " nothing " ] = nullptr ;
// add an object inside the object
j[ " answer " ][ " everything " ] = 42 ;
// add an array that is stored as std::vector (using an initializer list)
j[ " list " ] = { 1 , 0 , 2 };
// add another object (using an initializer list of pairs)
j[ " object " ] = { { " currency " , " USD " }, { " value " , 42.99 } };
// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
{ " pi " , 3.141 },
{ " happy " , true },
{ " name " , " Niels " },
{ " nothing " , nullptr },
{ " answer " , {
{ " everything " , 42 }
}},
{ " list " , { 1 , 0 , 2 }},
{ " object " , {
{ " currency " , " USD " },
{ " value " , 42.99 }
}}
};
请注意,在所有这些情况下,您永远不需要“告诉”编译器您要使用哪种 JSON 值类型。如果您想明确或表达一些边缘情况,函数json::array()
和json::object()
将有所帮助:
// a way to express the empty array []
json empty_array_explicit = json::array();
// ways to express the empty object {}
json empty_object_implicit = json({});
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ { " currency " , " USD " }, { " value " , 42.99 } });
您可以通过将_json
附加到字符串文字来创建 JSON 值(反序列化):
// create object from string literal
json j = " { " happy " : true, " pi " : 3.141 } " _json;
// or even nicer with a raw string literal
auto j2 = R"(
{
"happy": true,
"pi": 3.141
}
)" _json;
请注意,如果不附加_json
后缀,则不会解析传递的字符串文字,而只是用作 JSON 字符串值。也就是说, json j = "{ "happy": true, "pi": 3.141 }"
只会存储字符串"{ "happy": true, "pi": 3.141 }"
而不是解析实际的字符串目的。
应using namespace nlohmann::literals;
(参见json::parse()
)。
上面的例子也可以使用json::parse()
显式表达:
// parse explicitly
auto j3 = json::parse( R"( {"happy": true, "pi": 3.141} )" );
您还可以获取 JSON 值的字符串表示形式(序列化):
// explicit conversion to string
std::string s = j.dump(); // {"happy":true,"pi":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump( 4 ) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
注意序列化和赋值之间的区别:
// store a string in a JSON value
json j_string = " this is a string " ;
// retrieve the string value
auto cpp_string = j_string. template get<std::string>();
// retrieve the string value (alternative when a variable already exists)
std::string cpp_string2;
j_string.get_to(cpp_string2);
// retrieve the serialized value (explicit JSON serialization)
std::string serialized_string = j_string.dump();
// output of original string
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string. template get<std::string>() << ' n ' ;
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;
.dump()
返回原始存储的字符串值。
请注意,该库仅支持 UTF-8。当您在库中存储具有不同编码的字符串时,调用dump()
可能会引发异常,除非使用json::error_handler_t::replace
或json::error_handler_t::ignore
作为错误处理程序。
您还可以使用流来序列化和反序列化:
// deserialize from standard input
json j;
std::cin >> j;
// serialize to standard output
std::cout << j;
// the setw manipulator was overloaded to set the indentation for pretty printing
std::cout << std::setw( 4 ) << j << std::endl;
这些运算符适用于std::istream
或std::ostream
的任何子类。这是与文件相同的示例:
// read a JSON file
std::ifstream i ( " file.json " );
json j;
i >> j;
// write prettified JSON to another file
std::ofstream o ( " pretty.json " );
o << std::setw( 4 ) << j << std::endl;
请注意,为failbit
位设置异常位不适合此用例。由于使用了noexcept
说明符,它将导致程序终止。
您还可以从迭代器范围解析 JSON;也就是说,来自任何可由迭代器访问的容器,其value_type
是 1、2 或 4 字节的整数类型,这将分别解释为 UTF-8、UTF-16 和 UTF-32。例如, std::vector<std::uint8_t>
或std::list<std::uint16_t>
:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v.begin(), v.end());
您可以将迭代器保留在 [begin, end) 范围内:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v);
由于解析函数接受任意迭代器范围,因此您可以通过实现LegacyInputIterator
概念来提供自己的数据源。
struct MyContainer {
void advance ();
const char & get_current ();
};
struct MyIterator {
using difference_type = std:: ptrdiff_t ;
using value_type = char ;
using pointer = const char *;
using reference = const char &;
using iterator_category = std::input_iterator_tag;
MyIterator& operator ++() {
target-> advance ();
return * this ;
}
bool operator !=( const MyIterator& rhs) const {
return rhs. target != target;
}
reference operator *() const {
return target-> get_current ();
}
MyContainer* target = nullptr ;
};
MyIterator begin (MyContainer& tgt) {
return MyIterator{&tgt};
}
MyIterator end ( const MyContainer&) {
return {};
}
void foo () {
MyContainer c;
json j = json::parse (c);
}
该库使用类似 SAX 的接口,具有以下功能:
// called when null is parsed
bool null ();
// called when a boolean is parsed; value is passed
bool boolean ( bool val);
// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer ( number_integer_t val);
bool number_unsigned ( number_unsigned_t val);
// called when a floating-point number is parsed; value and original string is passed
bool number_float ( number_float_t val, const string_t & s);
// called when a string is parsed; value is passed and can be safely moved away
bool string ( string_t & val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary ( binary_t & val);
// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object (std:: size_t elements);
bool end_object ();
bool start_array (std:: size_t elements);
bool end_array ();
// called when an object key is parsed; value is passed and can be safely moved away
bool key ( string_t & val);
// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error (std:: size_t position, const std::string& last_token, const detail:: exception & ex);
每个函数的返回值决定是否应该继续解析。
要实现您自己的 SAX 处理程序,请按以下步骤操作:
nlohmann::json_sax<json>
作为基类,但也可以使用实现上述功能且公开的任何类。my_sax
。bool json::sax_parse(input, &my_sax)
;其中第一个参数可以是任何输入,例如字符串或输入流,第二个参数是指向 SAX 接口的指针。请注意, sax_parse
函数仅返回一个bool
指示上次执行的 SAX 事件的结果。它不返回json
值 - 由您决定如何处理 SAX 事件。此外,如果出现解析错误,则不会引发异常 - 由您决定如何处理传递给parse_error
实现的异常对象。在内部,SAX 接口用于 DOM 解析器(类json_sax_dom_parser
)以及接受器( json_sax_acceptor
),请参阅文件json_sax.hpp
。
我们设计的 JSON 类的行为就像 STL 容器一样。事实上,它满足了ReversibleContainer 的要求。
// create an array using push_back
json j;
j.push_back( " foo " );
j.push_back( 1 );
j.push_back( true );
// also use emplace_back
j.emplace_back( 1.78 );
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << ' n ' ;
}
// range-based for
for ( auto & element : j) {
std::cout << element << ' n ' ;
}
// getter/setter
const auto tmp = j[ 0 ]. template get<std::string>();
j[ 1 ] = 42 ;
bool foo = j.at( 2 );
// comparison
j == R"( ["foo", 1, true, 1.78] )" _json; // true
// other stuff
j.size(); // 4 entries
j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again
// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();
// create an object
json o;
o[ " foo " ] = 23 ;
o[ " bar " ] = false ;
o[ " baz " ] = 3.141 ;
// also use emplace
o.emplace( " weather " , " sunny " );
// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
std::cout << it. key () << " : " << it. value () << " n " ;
}
// the same code as range for
for ( auto & el : o.items()) {
std::cout << el. key () << " : " << el. value () << " n " ;
}
// even easier with structured bindings (C++17)
for ( auto & [key, value] : o.items()) {
std::cout << key << " : " << value << " n " ;
}
// find an entry
if (o.contains( " foo " )) {
// there is an entry with key "foo"
}
// or via find and an iterator
if (o.find( " foo " ) != o.end()) {
// there is an entry with key "foo"
}
// or simpler using count()
int foo_present = o.count( " foo " ); // 1
int fob_present = o.count( " fob " ); // 0
// delete an entry
o.erase( " foo " );
任何序列容器( std::array
、 std::vector
、 std::deque
、 std::forward_list
、 std::list
),其值可用于构造 JSON 值(例如整数、浮点数、布尔值、字符串)类型,或者本节中描述的 STL 容器)可用于创建 JSON 数组。这同样适用于类似的关联容器( std::set
、 std::multiset
、 std::unordered_set
、 std::unordered_multiset
),但在这些情况下,数组元素的顺序取决于元素在数组中的排序方式。各自的STL容器。
std::vector< int > c_vector { 1 , 2 , 3 , 4 };
json j_vec (c_vector);
// [1, 2, 3, 4]
std::deque< double > c_deque { 1.2 , 2.3 , 3.4 , 5.6 };
json j_deque (c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::list< bool > c_list { true , true , false , true };
json j_list (c_list);
// [true, true, false, true]
std::forward_list< int64_t > c_flist { 12345678909876 , 23456789098765 , 34567890987654 , 45678909876543 };
json j_flist (c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array< unsigned long , 4 > c_array {{ 1 , 2 , 3 , 4 }};
json j_array (c_array);
// [1, 2, 3, 4]
std::set<std::string> c_set { " one " , " two " , " three " , " four " , " one " };
json j_set (c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]
std::unordered_set<std::string> c_uset { " one " , " two " , " three " , " four " , " one " };
json j_uset (c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset { " one " , " two " , " one " , " four " };
json j_mset (c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset { " one " , " two " , " one " , " four " };
json j_umset (c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
同样,任何关联键值容器( std::map
、 std::multimap
、 std::unordered_map
、 std::unordered_multimap
),其键可以构造std::string
并且其值可用于构造 JSON 值(请参阅上面的示例)可用于创建 JSON 对象。请注意,在多重映射的情况下,JSON 对象中仅使用一个键,并且该值取决于 STL 容器的内部顺序。
std::map<std::string, int > c_map { { " one " , 1 }, { " two " , 2 }, { " three " , 3 } };
json j_map (c_map);
// {"one": 1, "three": 3, "two": 2 }
std::unordered_map< const char *, double > c_umap { { " one " , 1.2 }, { " two " , 2.3 }, { " three " , 3.4 } };
json j_umap (c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::multimap<std::string, bool > c_mmap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_mmap (c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
std::unordered_multimap<std::string, bool > c_ummap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_ummap (c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
该库支持JSON 指针(RFC 6901) 作为寻址结构化值的替代方法。除此之外, JSON Patch (RFC 6902) 允许描述两个 JSON 值之间的差异 - 有效地允许 Unix 已知的 patch 和 diff 操作。
// a JSON value
json j_original = R"( {
"baz": ["one", "two", "three"],
"foo": "bar"
} )" _json;
// access members with a JSON pointer (RFC 6901)
j_original[ " /baz/1 " _json_pointer];
// "two"
// a JSON patch (RFC 6902)
json j_patch = R"( [
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
] )" _json;
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff (j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op": "remove","path": "/hello" },
// { "op": "add", "path": "/foo", "value": "bar" }
// ]
该库支持JSON Merge Patch (RFC 7386) 作为补丁格式。它没有使用 JSON 指针(见上文)来指定要操作的值,而是使用非常模仿正在修改的文档的语法来描述更改。
// a JSON value
json j_document = R"( {
"a": "b",
"c": {
"d": "e",
"f": "g"
}
} )" _json;
// a patch
json j_patch = R"( {
"a":"z",
"c": {
"f": null
}
} )" _json;
// apply the patch
j_document.merge_patch(j_patch);
// {
// "a": "z",
// "c": {
// "d": "e"
// }
// }
支持的类型可以隐式转换为 JSON 值。
建议不要使用JSON 值的隐式转换。您可以在此处找到有关此建议的更多详细信息。您可以通过在包含json.hpp
标头之前将JSON_USE_IMPLICIT_CONVERSIONS
定义为0
来关闭隐式转换。使用 CMake 时,您还可以通过将选项JSON_ImplicitConversions
设置为OFF
来实现此目的。
// strings
std::string s1 = " Hello, world! " ;
json js = s1;
auto s2 = js. template get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;
// Booleans
bool b1 = true ;
json jb = b1;
auto b2 = jb. template get< bool >();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;
// numbers
int i = 42