那里有无数的JSON库,每个库甚至可能有其存在的理由。我们班有以下设计目标:
直观的语法。在Python等语言中,JSON感觉像是一流的数据类型。我们使用了现代C++的所有运算符魔力,在你的代码中实现了相同的感觉。看看下面的例子,你就会明白我的意思了。
琐碎的集成。我们的整个代码由一个头文件 json.hpp
组成。就是这样。没有库,没有子项目,没有依赖项,没有复杂的构建系统。该类是用 Vanilla C++11编写的。总而言之,一切都不需要调整编译器标志或项目设置。
认真测试。我们的代码经过严格的单元测试,涵盖了 100% 的代码,包括所有异常行为。此外,我们与Valgrind和Clang Sanitizers进行了检查,没有内存泄漏。Google OSS-Fuzz 还对所有解析器运行模糊测试,每周 7 天、每天 24 小时,到目前为止有效地执行了数十亿次测试。为了保持高质量,该项目遵循核心基础设施倡议(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 值的 API。要通过读取 JSON 文件创建对象,请执行以下操作:
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 }"
字符串文字应该被纳入范围(参见 json::p arse()
)。
using namespace nlohmann::literals;
上面的例子也可以用json::p arse()
显式表示:
// 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;也就是说,从迭代器可访问的任何容器中,迭代器是 1、2 或 4 字节的整数类型,将分别解释为 UTF-8、UTF-16 和 UTF-32。例如,a 或 :
value_type
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());
你可以将迭代器保留在范围 [开始, 结束):
std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
json j = json::parse(v);
由于 parse 函数接受任意迭代器范围,因此你可以通过实现该概念来提供自己的数据源。
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++() {
MyContainer.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 事件。此外,在发生解析错误时不会引发异常 - 如何处理传递给实现的异常对象取决于你。在内部,SAX 接口用于 DOM 解析器(类)和接受器(),请参阅文件 json_sax.hpp
。
sax_parse
bool
json
parse_error
json_sax_dom_parser
json_sax_acceptor
我们将 JSON 类设计为类似于 STL 容器的行为。事实上,它满足可逆容器的要求。
// 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");
任何其值可用于构造 JSON 值(例如,整数、浮点数、布尔值、字符串类型或本节中描述的 STL 容器)的序列容器 (、、) 都可用于创建 JSON 数组。这同样适用于类似的关联容器 (, , , ),但在这些情况下,数组元素的顺序取决于元素在相应 STL 容器中的排序方式。
std::array
std::vector
std::deque
std::forward_list
std::list
std::set
std::multiset
std::unordered_set
std::unordered_multiset
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"]
同样,任何关联键值容器 (, , , ) 其键可以构造 并且其值可用于构造 JSON 值(请参阅上面的示例)都可用于创建 JSON 对象。请注意,在多重映射的情况下,JSON 对象中仅使用一个键,该值取决于 STL 容器的内部顺序。
std::map
std::multimap
std::unordered_map
std::unordered_multimap
std::string
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已知的补丁和差异操作。
// 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 合并修补程序 (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 值的隐式转换。你可以在此处找到有关此建议的更多详细信息。你可以通过在包含标头之前定义 to 来关闭隐式转换。使用 CMake 时,还可以通过将选项设置为 .
JSON_USE_IMPLICIT_CONVERSIONS
0
json.hpp
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;
json jn = i;
auto f = jn.template get<double>();
// NOT RECOMMENDED
double f2 = jb;
double f3;
f3 = jb;
// etc.
请注意,类型不会自动转换为 JSON 字符串,而是转换为整数。必须显式指定对字符串的转换:
char
char ch = 'A'; // ASCII value 65
json j_default = ch; // stores integer number 65
json j_string = std::string(1, ch); // stores string "A"
每个类型都可以在 JSON 中序列化,而不仅仅是 STL 容器和标量类型。通常,你会按照这些思路做一些事情:
namespace ns {
// a simple struct to model a person
struct person {
std::string name;
std::string address;
int age;
};
}
ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};
// convert to JSON: copy each value into the JSON object
json j;
j["name"] = p.name;
j["address"] = p.address;
j["age"] = p.age;
// ...
// convert from JSON: copy each value from the JSON object
ns::person p {
j["name"].template get<std::string>(),
j["address"].template get<std::string>(),
j["age"].template get<int>()
};
它有效,但这是相当多的样板...幸运的是,有一个更好的方法:
// create a person
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
// conversion: person -> json
json j = p;
std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
// conversion: json -> person
auto p2 = j.template get<ns::person>();
// that's it
assert(p == p2);
要使其适用于你的一种类型,你只需要提供两个函数:
using json = nlohmann::json;
namespace ns {
void to_json(json& j, const person& p) {
j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
}
void from_json(const json& j, person& p) {
j.at("name").get_to(p.name);
j.at("address").get_to(p.address);
j.at("age").get_to(p.age);
}
} // namespace ns
就这样!使用类型调用构造函数时,将自动调用自定义方法。同样,调用 or 时,将调用该方法。
json
to_json
template get<your_type>()
get_to(your_type&)
from_json
一些重要的事情:
ns
person
template get<your_type>()
your_type
at()
而不是 访问对象值。如果键不存在,则引发可以处理的异常,而表现出未定义的行为。from_json
operator[]
at
operator[]
std::vector
如果你只想序列化/反序列化一些结构,/ 函数可以是很多样板。
to_json
from_json
有两个宏可以让你的生活更轻松,只要你 (1) 想要使用 JSON 对象作为序列化,并且 (2) 想要使用成员变量名称作为该对象中的对象键:
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)将在要为其创建代码的类/结构的命名空间内定义。
NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)将在要为其创建代码的类/结构中定义。此宏还可以访问私有成员。
在这两个宏中,第一个参数是类/结构的名称,其余所有参数都命名成员。
上述结构的 / 函数可以通过以下方式创建:
to_json
from_json
person
namespace ns {
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
}
下面是一个需要私有成员的示例:
NLOHMANN_DEFINE_TYPE_INTRUSIVE
namespace ns {
class address {
private:
std::string street;
int housenumber;
int postcode;
public:
NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode)
};
}
这需要更先进的技术。但首先,让我们看看这种转换机制是如何工作的:
该库使用 JSON 序列化程序将类型转换为 json。的默认序列化程序是(ADL 表示 依赖于参数的查找)。
nlohmann::json
nlohmann::adl_serializer
它是这样实现的(简化):
template <typename T>
struct adl_serializer {
static void to_json(json& j, const T& value) {
// calls the "to_json" method in T's namespace
}
static void from_json(const json& j, T& value) {
// same thing, but with the "from_json" method
}
};
当你控制类型的命名空间时,此序列化程序工作正常。但是,或(C++17)呢?劫持命名空间非常糟糕,将模板专用化以外的内容添加到...
boost::optional
std::filesystem::path
boost
std
要解决此问题,你需要向命名空间添加专用化,下面是一个示例:
adl_serializer
nlohmann
// partial specialization (full specialization works too)
namespace nlohmann {
template <typename T>
struct adl_serializer<boost::optional<T>> {
static void to_json(json& j, const boost::optional<T>& opt) {
if (opt == boost::none) {
j = nullptr;
} else {
j = *opt; // this will call adl_serializer<T>::to_json which will
// find the free function to_json in T's namespace!
}
}
static void from_json(const json& j, boost::optional<T>& opt) {
if (j.is_null()) {
opt = boost::none;
} else {
opt = j.template get<T>(); // same as above, but with
// adl_serializer<T>::from_json
}
}
};
}
get() 用于非默认的可构造/不可复制类型?
有一种方法,如果你的类型是MoveConstructible。你还需要专门化,但具有特殊的重载:
adl_serializer
from_json
struct move_only_type {
move_only_type() = delete;
move_only_type(int ii): i(ii) {}
move_only_type(const move_only_type&) = delete;
move_only_type(move_only_type&&) = default;
int i;
};
namespace nlohmann {
template <>
struct adl_serializer<move_only_type> {
// note: the return type is no longer 'void', and the method only takes
// one argument
static move_only_type from_json(const json& j) {
return {j.template get<int>()};
}
// Here's the catch! You must provide a to_json method! Otherwise, you
// will not be able to convert move_only_type to json, since you fully
// specialized adl_serializer on that type
static void to_json(json& j, move_only_type t) {
j = t.i;
}
};
}
是的。你可能想看看测试套件中的 unit-udt.cpp
以查看一些示例。
如果编写自己的序列化程序,则需要执行以下几项操作:
basic_json
nlohmann::json
basic_json
JSONSerializer)
basic_json
to_json
from_json
nlohmann::to_json
nlohmann::from_json
下面是一个示例,无需简化,它仅接受大小为 <= 32 的类型,并使用 ADL。
// You should use void as a second template argument
// if you don't need compile-time checks on T
template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type>
struct less_than_32_serializer {
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, T value) {
// we want to use ADL, and call the correct to_json overload
using nlohmann::to_json; // this method is called by adl_serializer,
// this is where the magic happens
to_json(j, value);
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, T& value) {
// same thing here
using nlohmann::from_json;
from_json(j, value);
}
};
重新实现序列化程序时要非常小心,如果不注意,可能会堆叠溢出:
template <typename T, void>
struct bad_serializer
{
template <typename BasicJsonType>
static void to_json(BasicJsonType& j, const T& value) {
// this calls BasicJsonType::json_serializer<T>::to_json(j, value);
// if BasicJsonType::json_serializer == bad_serializer ... oops!
j = value;
}
template <typename BasicJsonType>
static void to_json(const BasicJsonType& j, T& value) {
// this calls BasicJsonType::json_serializer<T>::from_json(j, value);
// if BasicJsonType::json_serializer == bad_serializer ... oops!
value = j.template get<T>(); // oops!
}
};
默认情况下,枚举值作为整数序列化为 JSON。在某些情况下,这可能会导致意外行为。如果在数据序列化为 JSON 后修改或重新排序枚举,则稍后反序列化的 JSON 数据可能未定义或枚举值与最初预期不同。
可以更精确地指定给定枚举如何映射到 JSON 以及从 JSON 映射,如下所示:
// example enum type declaration
enum TaskState {
TS_STOPPED,
TS_RUNNING,
TS_COMPLETED,
TS_INVALID=-1,
};
// map TaskState values to JSON as strings
NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, {
{TS_INVALID, nullptr},
{TS_STOPPED, "stopped"},
{TS_RUNNING, "running"},
{TS_COMPLETED, "completed"},
})
宏为类型声明一组 / 函数,同时避免重复和样板序列化代码。
NLOHMANN_JSON_SERIALIZE_ENUM()
to_json()
from_json()
TaskState
用法:
// enum to JSON as string
json j = TS_STOPPED;
assert(j == "stopped");
// json string to enum
json j3 = "running";
assert(j3.template get<TaskState>() == TS_RUNNING);
// undefined json value to enum (where the first map entry above is the default)
json jPi = 3.14;
assert(jPi.template get<TaskState>() == TS_INVALID );
就像上面的任意类型转换一样,
NLOHMANN_JSON_SERIALIZE_ENUM()必须在枚举类型的命名空间(可以是全局命名空间)中声明,否则库将无法找到它,并且它将默认为整数序列化。
其他要点:
template get<ENUM_TYPE>()
虽然JSON是一种无处不在的数据格式,但它不是一种非常紧凑的格式,适合数据交换,例如通过网络。因此,该库支持BSON(二进制JSON),CBOR(简洁二进制对象表示),MessagePack,UBJSON(通用二进制JSON规范)和BJData(二进制JData),以有效地将JSON值编码为字节向量并解码此类向量。
// create a JSON value
json j = R"({"compact": true, "schema": 0})"_json;
// serialize to BSON
std::vector<std::uint8_t> v_bson = json::to_bson(j);
// 0x1B, 0x00, 0x00, 0x00, 0x08, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x00, 0x01, 0x10, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
// roundtrip
json j_from_bson = json::from_bson(v_bson);
// serialize to CBOR
std::vector<std::uint8_t> v_cbor = json::to_cbor(j);
// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00
// roundtrip
json j_from_cbor = json::from_cbor(v_cbor);
// serialize to MessagePack
std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);
// 0x82, 0xA7, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xC3, 0xA6, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00
// roundtrip
json j_from_msgpack = json::from_msgpack(v_msgpack);
// serialize to UBJSON
std::vector<std::uint8_t> v_ubjson = json::to_ubjson(j);
// 0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D
// roundtrip
json j_from_ubjson = json::from_ubjson(v_ubjson);
该库还支持来自BSON,CBOR(字节字符串)和MessagePack(bin,ext,fixext)的二进制类型。默认情况下,它们被存储为在库外部处理。
std::vector<std::uint8_t>
// CBOR byte string with payload 0xCAFE
std::vector<std::uint8_t> v = {0x42, 0xCA, 0xFE};
// read value
json j = json::from_cbor(v);
// the JSON value has type binary
j.is_binary(); // true
// get reference to stored binary value
auto& binary = j.get_binary();
// the binary value has no subtype (CBOR has no binary subtypes)
binary.has_subtype(); // false
// access std::vector<std::uint8_t> member functions
binary.size(); // 2
binary[0]; // 0xCA
binary[1]; // 0xFE
// set subtype to 0x10
binary.set_subtype(0x10);
// serialize to MessagePack
auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE
虽然已经是 2023 年了,但对 C++11 的支持仍然有点稀疏。目前,已知以下编译器可以工作:
我很乐意了解其他编译器/版本。
请注意:
GCC 4.8 有一个错误 57824):多行原始字符串不能是宏的参数。不要使用此编译器直接在宏中使用多行原始字符串。
Android 默认使用非常旧的编译器和C++库。要解决此问题,请将以下内容添加到你的 .这将切换到 LLVM C++库、Clang 编译器,并启用 C++11 和其他默认禁用的功能。
Application.mk
APP_STL := c++_shared NDK_TOOLCHAIN_VERSION := clang3.6 APP_CPPFLAGS += -frtti -fexceptions
该代码与Android NDK修订版9 - 11(可能更高版本)和CrystaX的Android NDK版本10成功编译。
对于在MinGW或Android SDK上运行的GCC,可能会出现错误(或类似的,for或)。请注意,这不是代码的问题,而是编译器本身的问题。在 Android 上,请参阅上文以使用较新的环境进行构建。对于 MinGW,请参阅此站点和此讨论以获取有关如何修复此错误的信息。对于使用 的 Android NDK,请参阅此讨论。
'to_string' is not a member of 'std'
strtod
strtof
APP_STL := gnustl_static
不受支持的 GCC 和 Clang 版本被指令拒绝。这可以通过定义来关闭。请注意,在这种情况下,你可以期望不支持。
#error
JSON_SKIP_UNSUPPORTED_COMPILER_CHECK
以下编译器目前用于 AppVeyor、Cirrus CI 和 GitHub Actions 的持续集成:
编译器 | 操作系统 | CI 提供程序 |
---|---|---|
苹果叮当 11.0.3 (clang-1103.0.32.62);Xcode 11.7 | macOS 11.7.1 | GitHub 操作 |
苹果叮当 12.0.0 (clang-1200.0.32.29);Xcode 12.4 | macOS 11.7.1 | GitHub 操作 |
苹果叮当 12.0.5 (叮当-1205.0.22.11);Xcode 12.5.1 | macOS 11.7.1 | GitHub 操作 |
苹果叮当 13.0.0 (叮当-1300.0.29.3);Xcode 13.0 | macOS 11.7.1 | GitHub 操作 |
苹果叮当 13.0.0 (叮当-1300.0.29.3);Xcode 13.1 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 13.0.0 (叮当-1300.0.29.30);Xcode 13.2.1 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 13.1.6 (clang-1316.0.21.2.3);Xcode 13.3.1 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 13.1.6 (叮当-1316.0.21.2.5);Xcode 13.4.1 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 14.0.0 (叮当-1400.0.29.102);Xcode 14.0 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 14.0.0 (叮当-1400.0.29.102);Xcode 14.0.1 | macOS 12.6.1 | GitHub 操作 |
苹果叮当 14.0.0 (clang-1400.0.29.202);Xcode 14.1 | macOS 12.6.1 | GitHub 操作 |
叮当 3.5.2 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 3.6.2 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 3.7.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 3.8.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 3.9.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 4.0.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 5.0.2 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 6.0.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 7.0.1 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 8.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 9.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 10.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Clang 10.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
Clang 11.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
Clang 11.0.0 与类似 MSVC 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
叮当 11.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
叮当 12.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Clang 12.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
叮当 13.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Clang 13.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
叮当 14.0.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Clang 14.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
Clang 15.0.0 与类似 GNU 的命令行 | 视窗-10.0.17763 | GitHub 操作 |
叮当 15.0.4 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Clang 16.0.0 (16.0.0-++20221031071727+500876226c60-1 |
优麒麟 20.04.3 LTS | GitHub 操作 |
GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2) | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 4.9.4 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 5.5.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 6.5.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 7.5.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
GCC 8.1.0 (i686-posix-dwarf-rev0,由MinGW-W64项目构建) | 视窗-10.0.17763 | GitHub 操作 |
GCC 8.1.0 (x86_64-posix-she-rev0, 由MinGW-W64项目构建) | 视窗-10.0.17763 | GitHub 操作 |
海湾合作委员会 8.5.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 9.5.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 10.4.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 11.1.0 | Ubuntu (aarch64) | 卷云CI |
海湾合作委员会 11.3.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
海湾合作委员会 12.2.0 | 优麒麟 20.04.3 LTS | GitHub 操作 |
GCC 13.0.0 20220605(实验性) | 优麒麟 20.04.3 LTS | GitHub 操作 |
英特尔C++编译器 2021.5.0.20211109 | 优麒麟 20.04.3 LTS | GitHub 操作 |
NVCC 11.0.221 | 优麒麟 20.04.3 LTS | GitHub 操作 |
Visual Studio 14 2015 MSVC 19.0.24241.7(生成引擎版本 14.0.25420.1) | 视窗-6.3.9600 | AppVeyor |
Visual Studio 15 2017 MSVC 19.16.27035.0(Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | 视窗-10.0.14393 | AppVeyor |
Visual Studio 16 2019 MSVC 19.28.29912.0(Build Engine version 16.9.0+57a23d249 for .NET Framework) | 视窗-10.0.17763 | GitHub 操作 |
Visual Studio 16 2019 MSVC 19.28.29912.0(Build Engine version 16.9.0+57a23d249 for .NET Framework) | 视窗-10.0.17763 | AppVeyor |
Visual Studio 17 2022 MSVC 19.30.30709.0(适用于 .NET Framework 的构建引擎版本 17.0.31804.368) | 视窗-10.0.20348 | GitHub 操作 |
json.hpp
是此处发布或发布的唯一必需文件。你需要添加
single_include/nlohmann
#include <nlohmann/json.hpp>
// for convenience
using json = nlohmann::json;
到要处理 JSON 的文件,并设置必要的开关以启用 C++11(例如,用于 GCC 和 Clang)。
-std=c++11
你可以进一步使用文件 include/nlohmann/json_fwd.hpp
进行前向声明。json_fwd.hpp 的安装(作为 cmake 安装步骤的一部分),可以通过设置 .
-DJSON_MultipleHeaders=ON
你还可以在 CMake 中使用接口目标。此目标填充相应的使用要求,以指向相应的包含目录和必要的 C++11 标志。
nlohmann_json::nlohmann_json
INTERFACE_INCLUDE_DIRECTORIES
INTERFACE_COMPILE_FEATURES
若要从 CMake 项目中使用此库,可以直接使用生成的包配置中导入的命名空间目标找到它并使用它:
find_package()
# CMakeLists.txt
find_package(nlohmann_json 3.2.0 REQUIRED)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
软件包配置文件 可以从安装树中使用,也可以直接从构建树中使用。
nlohmann_jsonConfig.cmake
要将库直接嵌入到现有 CMake 项目中,请将整个源代码树放在子目录中并调用文件:
add_subdirectory()
CMakeLists.txt
# Typically you don't care so much for a third party library's tests to be
# run from your own project's code.
set(JSON_BuildTests OFF CACHE INTERNAL "")
# If you only include this third party in PRIVATE source files, you do not
# need to install it when your main project gets installed.
# set(JSON_Install OFF CACHE INTERNAL "")
# Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it
# unintended consequences that will break the build. It's generally
# discouraged (although not necessarily well documented as such) to use
# include(...) for pulling in other CMake projects anyways.
add_subdirectory(nlohmann_json)
...
add_library(foo ...)
...
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
从 CMake v3.11 开始,FetchContent 可用于在配置时自动下载版本作为依赖项。
例:
include(FetchContent)
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz)
FetchContent_MakeAvailable(json)
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
注意:建议使用上述 URL 方法,该方法从版本 3.10.0 开始受支持。有关详细信息,请参阅 https://json.nlohmann.me/integration/cmake/#fetchcontent。
要允许项目支持外部提供的或嵌入式 JSON 库,可以使用类似于以下内容的模式:
# Top level CMakeLists.txt
project(FOO)
...
option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF)
...
add_subdirectory(thirdparty)
...
add_library(foo ...)
...
# Note that the namespaced target will always be available regardless of the
# import method
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
# thirdparty/CMakeLists.txt
...
if(FOO_USE_EXTERNAL_JSON)
find_package(nlohmann_json 3.2.0 REQUIRED)
else()
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
endif()
...
thirdparty/nlohmann_json然后是此源代码树的完整副本。
🍺 如果你使用的是OS X和Homebrew,只需键入即可设置。如果需要最前沿而不是最新版本,请使用 。有关更多信息,请参阅 nlohmann-json。
brew install nlohmann-json
brew install nlohmann-json --HEAD
如果你使用的是介子构建系统,请将此源代码树添加为介子子项目。你还可以使用此项目版本中发布的版本来减小供应商源代码树的大小。或者,你可以通过从 Meson WrapDB 下载一个包装文件来获取它,或者简单地使用 .有关包装的任何问题,请参阅介子项目。
include.zip
meson wrap install nlohmann_json
提供的也可以用作 CMake 的替代方法,用于在系统范围内安装,在这种情况下,将安装 pkg 配置文件。要使用它,只需让你的构建系统需要 pkg 配置依赖项即可。在 Meson 中,最好将 dependency()
对象与子项目回退一起使用,而不是直接使用子项目。
meson.build
nlohmann_json
nlohmann_json
如果你使用的是 Bazel,则可以简单地使用 或 引用此存储库,并依赖于 。
http_archive
git_repository
@nlohmann_json//:json
如果使用 Conan 管理依赖项,只需将 nlohmann_json/x.y.z
添加到 的要求中,即要使用的发布版本在哪里。如果你在使用软件包时遇到问题,请在此处提交问题。
conanfile
x.y.z
如果你使用 Spack 来管理依赖项,则可以使用 nlohmann-json 包
。有关包装的任何问题,请参阅spack项目。
如果在项目上使用 hunter 作为外部依赖项,则可以使用 nlohmann_json 包。有关包装的任何问题,请参阅猎人项目。
如果你使用的是 Buckaroo,则可以使用 .请在此处提交问题。这里有一个演示存储库。
buckaroo add github.com/buckaroo-pm/nlohmann-json
如果你在项目上使用 vcpkg 作为外部依赖项,则可以使用 并按照当时显示的说明安装 nlohmann-json 包。有关打包的任何问题,请参阅 vcpkg 项目。
vcpkg install nlohmann-json
如果你使用的是 cget,则可以使用 .可以使用 安装特定版本。此外,可以通过添加标志(即 )来安装多标头版本。
cget install nlohmann/json
cget install nlohmann/json@v3.1.0
-DJSON_MultipleHeaders=ON
cget install nlohmann/json -DJSON_MultipleHeaders=ON
如果你使用的是 CocoaPods,则可以通过将 pod 添加到 pod 文件来使用该库(请参阅示例)。请在此处提交问题。
"nlohmann_json", '~>3.1.2'
如果使用的是NuGet,则可以使用包nlohmann.json。请查看有关如何使用该软件包的广泛说明。请在此处提交问题。
如果你使用的是 conda,则可以使用 conda-forge 执行中的包nlohmann_json。请在此处提交问题。
conda install -c conda-forge nlohmann_json
如果你使用的是MSYS2,你可以使用mingw-w64-nlohmann-json包,只需键入或进行安装。如果你在使用软件包时遇到问题,请在此处提交问题。
pacman -S mingw-w64-i686-nlohmann-json
pacman -S mingw-w64-x86_64-nlohmann-json
如果你使用的是 MacPorts,请执行以安装 nlohmann-json 软件包。
sudo port install nlohmann-json
如果你使用的是 build2
,则可以从公共存储库 https://cppget.org 或直接从包的源存储库使用 nlohmann-json
包。在项目的文件中,只需添加(可能有一些版本限制)。如果你不熟悉在 中使用依赖项,请阅读此介绍。如果你在使用软件包时遇到问题,请在此处提交问题。
manifest
depends: nlohmann-json
build2
如果你使用的是 wsjcpp
,则可以使用该命令获取最新版本。请注意,你可以将分支“:d evelop”更改为现有标记或其他分支。
wsjcpp install "https://github.com/nlohmann/json:develop"
如果你使用的是CPM.cmake
,则可以查看此示例
。将 CPM 脚本添加到项目后,将以下代码片段实现到 CMake:
CPMAddPackage(
NAME nlohmann_json
GITHUB_REPOSITORY nlohmann/json
VERSION 3.9.1)
如果你使用的是裸生成文件,则可以使用 生成指向库安装位置的包含标志:
pkg-config
pkg-config nlohmann_json --cflags
Meson构建系统的用户也将能够使用系统范围的库,该库可通过以下方式找到:
pkg-config
json = dependency('nlohmann_json', required: true)
该类根据 MIT 许可证获得许可:
版权所有 © 2013-2022 尼尔斯·罗曼
特此免费授予获得本软件和相关文档文件(“软件”)副本的任何人不受限制地处理本软件的权限,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,并允许向其提供本软件的人这样做, 须符合以下条件:
上述版权声明和本许可声明应包含在软件的所有副本或大部分内容中。
本软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于适销性、特定用途适用性和不侵权的保证。在任何情况下,作者或版权所有者均不对因本软件或本软件的使用或其他交易而引起、产生或与之相关的任何索赔、损害赔偿或其他责任负责,无论是在合同、侵权或其他诉讼中。
该类包含来自Bjoern Hoehrmann的UTF-8解码器,该解码器根据MIT许可证获得许可(见上文)。版权所有 © 2008-2009 比约恩·霍尔曼 bjoern@hoehrmann.de
该类包含来自Florian Loitsch的Grisu2算法的略微修改版本,该算法在MIT许可证下获得许可(见上文)。版权所有 © 2009 弗洛里安·洛伊奇
该类包含来自 Evan Nemerson 的 Hedley 副本,该副本的许可为 CC0-1.0。
该类包含Google Abseil的部分内容,该部分内容在Apache 2.0许可证下获得许可。
如果你对库有疑问,我想邀请你在 GitHub 上提出一个问题。请尽可能详细地描述你的请求、问题或疑问,并提及你正在使用的库版本以及编译器和操作系统的版本。在 GitHub 上打开问题允许此库的其他用户和贡献者进行协作。例如,我对MSVC几乎没有经验,这方面的大多数问题都已由不断增长的社区解决。如果你查看已关闭的问题,你会发现我们在大多数情况下 React 非常及时。
仅当你的请求包含机密信息时,请给我发送电子邮件。对于加密邮件,请使用此密钥。
Niels Lohmann 的提交和版本使用此 PGP 密钥进行签名。
我非常感谢以下人员的帮助。
parse()
get_ref()
has_mapped_type
int64_t
uint64_t
std::multiset
dump()
std::locale::classic()
以避免过多的语言环境慢跑,在解析器中发现了一些不错的性能改进,改进了基准代码,并实现了与语言环境无关的数字解析和打印。nullptr
-Weffc++
std::min
<iostream>
<iosfwd>
find()
count()
.natvis
std::stringstream
items()
JSON_INTERNAL_CATCH
find_package
NLOHMANN_JSON_SERIALIZE_ENUM
FILE*
operator/
contains()
push_back()
pop_back()
to_string
contains
json_pointer::back
items()
std::pair
std::tuple
json
FetchContent
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
NLOHMANN_DEFINE_TYPE_INTRUSIVE
nlohmann::ordered_json
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
NLOHMANN_DEFINE_TYPE_INTRUSIVE
string_view
FetchContent
-Wfloat-equal
-Wswitch-enum
-Wextra-semi-stmt warnings
-Wunused
JSON_DIAGNOSTICS
JSON_DIAGNOSTICS
operator[]
parse
std::string_view
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
meta()
std::filesystem
CHECK_THROWS_WITH_AS
<windows.h>
.pc
.cmake
share
patch_inplace
.gitignore
std::iterator_traits
JSON_NO_ENUM
include.zip
ensure_ascii
dump
sed
iterator_traits
operator/=
operator/
非常感谢你的帮助!如果我忘记了某人,请告诉我。
库本身由一个根据 MIT 许可证许可的头文件组成。但是,它是构建,测试,记录的,以及不使用大量第三方工具和服务的内容。多谢!
该库目前用于Apple macOS Sierra-Monterey和iOS 10-15。我不确定他们使用该库的目的是什么,但我很高兴它在这么多设备上运行。
该库支持 Unicode 输入,如下所示:
std::u16string并且可以解析,分别假设 UTF-16 和 UTF-32 编码。从文件或其他输入容器读取时不支持这些编码。
std::u32string
\uDEAD
std::string
dump()
可能会引发异常,除非 或 用作错误处理程序。json::error_handler_t::replace
json::error_handler_t::ignore
std::wstring
std::string
默认情况下,此库不支持注释。它这样做有三个原因:
注释不是 JSON 规范的一部分。你可能会争辩说,在 JavaScript 中是允许的,但 JSON 不是 JavaScript。
//
/* */
这不是一个疏忽:道格拉斯·克罗克福德(Douglas Crockford)在2012年5月写道:
我从JSON中删除了注释,因为我看到人们正在使用它们来保存解析指令,这种做法会破坏互操作性。我知道缺乏评论会让一些人感到难过,但不应该。
假设你使用 JSON 来保存要注释的配置文件。继续并插入你喜欢的所有评论。然后通过 JSMin 通过管道将其传递给你的 JSON 解析器。
如果某些库会添加注释支持,而其他库则不会,那么互操作性是危险的。请查看鲁棒性原则的有害后果。
但是,你可以在函数中将 set 参数传递给 true 以忽略或注释。然后,注释将被视为空格。
ignore_comments
parse
//
/* */
默认情况下,库不保留对象元素的插入顺序。这是符合标准的,因为 JSON 标准将对象定义为“零个或多个名称/值对的无序集合”。
如果你确实想保留广告顺序,可以尝试使用 nlohmann::ordered_json
类型。或者,你可以使用更复杂的有序映射,如 tsl::ordered_map
(集成)或 nlohmann::fifo_map
(集成)。
我们与Valgrind和地址清理器(ASAN)进行了检查,没有内存泄漏。
如果发现带有此库的解析程序不释放内存,请考虑以下情况,它可能与此库无关。
你的程序是使用 glibc 编译的。glibc 使用一个可调阈值来决定是实际将内存返回到系统还是缓存内存以供以后重用。如果你在程序中进行了大量小分配,并且这些小分配不是连续块并且可能低于阈值,那么它们将不会返回到操作系统。这是一个相关的问题#1924。
断言的文档
。特别是,note operator[]
实现了对 const 对象的未经检查的访问:如果给定的键不存在,则行为是未定义的(想想取消引用的空指针),如果断言打开,则会导致断言失败。如果不确定对象中的元素是否存在,请将 checked 访问与 at() 函数
一起使用。此外,还可以定义替换对 的调用。NDEBUG
JSON_ASSERT(x)
assert(x)
double
-fno-rtti
what()
异常字符串不适用于 MSVC,请参阅 #2824。-fno-exceptions
JSON_NOEXCEPTION
abort()
JSON_THROW_USER
throw
JSON_TRY_USER
try
JSON_CATCH_USER
catch
JSON_THROW_USER
要编译和运行测试,你需要执行
$ mkdir build
$ cd build
$ cmake .. -DJSON_BuildTests=On
$ cmake --build .
$ ctest --output-on-failure
请注意,在此阶段,将从外部存储库下载多个 JSON 测试文件。如果策略禁止在测试期间下载项目,则可以自行下载文件,并将包含测试文件的目录传递给 CMake。然后,不需要互联网连接。有关详细信息,请参阅问题 #2189。
ctest
-DJSON_TestDataDirectory=path
如果未找到测试套件,则多个测试套件将失败,如下所示:
=============================================================================== json/tests/src/make_test_data_available.hpp:21: TEST CASE: check test suite is downloaded json/tests/src/make_test_data_available.hpp:23: FATAL ERROR: REQUIRE( utils::check_testsuite_downloaded() ) is NOT correct! values: REQUIRE( false ) logged: Test data not found in 'json/cmake-build-debug/json_test_data'. Please execute target 'download_test_data' before running this test suite. See <https://github.com/nlohmann/json#execute-unit-tests> for more information. ===============================================================================
如果你下载了库而不是通过 Git 签出代码,则测试将失败。请执行以跳过这些测试。有关详细信息,请参阅问题 #2189。
cmake_fetch_content_configure
ctest -LE git_required
一些测试会更改已安装的文件,从而使整个过程不可重现。请执行以跳过这些测试。有关详细信息,请参阅问题 #2324。
ctest -LE not_reproducible
请注意,你需要调用以排除这两个标签。有关详细信息,请参阅问题 #2596。
cmake -LE "not_reproducible|git_required"
由于英特尔编译器默认使用不安全的浮点优化,因此单元测试可能会失败。然后使用标志 /fp:precision
。