json - 新一代 C++JSON库

Created at: 2013-07-04 16:47:49
开发语言: C++
授权协议: MIT

适用于现代C++的 JSON

构建状态 乌班图 苹果操作系统 窗户 承保状态 封面扫描构建状态 科达西徽章 语言等级:C/C++ 模糊测试状态 在线试用 文档 许可证 发布 包装状态 下载 问题 解决问题的平均时间 信息集成电路最佳实践 赞助商 重用状态 不和

设计目标

那里有无数的JSON库,每个库甚至可能有其存在的理由。我们的班级有以下设计目标:

  • 直观的语法。在Python等语言中,JSON感觉就像是一流的数据类型。我们利用现代C++的所有运算符魔力,在代码中实现了相同的感觉。看看下面的例子,你就会明白我的意思。

  • 琐碎的集成。我们的整个代码由一个头文件 json.hpp 组成。就是这样。没有库,没有子项目,没有依赖关系,没有复杂的构建系统。该类是用 Vanilla C++11编写的。总而言之,所有内容都不需要调整编译器标志或项目设置。

  • 认真的测试。我们的代码经过了严格的单元测试,涵盖了100%的代码,包括所有异常行为。此外,我们与瓦尔格林克朗消毒剂进行了检查,以确保没有内存泄漏。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 赞助者处赞助此库。

🏢企业赞助商

🏷️指定赞助商

谢谢大家!

支持

如果你有问题,请检查是否已在常见问题解答问答部分中回答。如果没有,请在那里提出一个新问题

📚如果要了解有关如何使用库的详细信息,请查看自述文件的其余部分,查看代码示例,或浏览帮助页面

🚧如果你想更好地了解 API,请查看 API 参考

🐛如果你发现了错误,请检查常见问题解答,如果它是已知问题或设计决策的结果。在创建新问题之前,请同时查看问题列表。请提供尽可能多的信息,以帮助我们理解和重现你的问题。

还有一个文档用于文档浏览器 Dash速度和 Zeal,其中包含作为脱机资源的完整文档

例子

下面是一些示例,可以让你了解如何使用该类。

除了以下示例之外,你可能还需要:

→ 查看文档
→浏览独立示例文件

每个 API 函数(记录在 API 文档中)都有一个相应的独立示例文件。例如,嵌入() 函数具有匹配的.cpp示例文件。

从文件中读取 JSON

该类提供用于操作 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 值作为对象进行硬编码:

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 作为一级数据类型

下面是一些示例,可以让你了解如何使用该类。

假设你要创建 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.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.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。例如,一个 或一个 :

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 处理程序,请按以下步骤操作:

  1. 在类中实现 SAX 接口。你可以使用 class 作为基类,但也可以使用实现和公开上述函数的任何类。
    nlohmann::json_sax<json>
  2. 创建 SAX 接口类的对象,例如 。
    my_sax
  3. 呼叫 ;其中,第一个参数可以是任何输入,如字符串或输入流,第二个参数是指向 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

类似 STL 的访问

我们将 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].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");

从 STL 容器转换

任何序列容器(, , , ,),其值可用于构造 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 指针和 JSON 修补程序

该库支持 JSON 指针RFC 6901) 作为解决结构化值的替代方法。除此之外,JSON补丁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 合并补丁

该库支持 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 值的隐式转换。你可以在此处找到有关此建议的更多详细信息。你可以通过在包含标头之前定义来关闭隐式转换。使用 CMake 时,还可以通过将选项设置为 来实现此目的。

JSON_USE_IMPLICIT_CONVERSIONS
0
json.hpp
JSON_ImplicitConversions
OFF

// strings
std::string s1 = "Hello, world!";
json js = s1;
auto s2 = js.get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;

// Booleans
bool b1 = true;
json jb = b1;
auto b2 = jb.get<bool>();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;

// numbers
int i = 42;
json jn = i;
auto f = jn.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"].get<std::string>(),
    j["address"].get<std::string>(),
    j["age"].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.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

就这样!使用类型调用构造函数时,将自动调用自定义方法。同样,在调用 或 时,将调用该方法。

json
to_json
get<your_type>()
get_to(your_type&)
from_json

一些重要的事情:

  • 这些方法必须位于类型的命名空间(可以是全局命名空间)中,否则库将无法找到它们(在此示例中,它们位于命名空间中,其中定义了)。
    ns
    person
  • 这些方法必须在使用这些转换的任何地方都可用(例如,必须包含正确的标头)。查看问题 1108 以了解否则可能发生的错误。
  • 使用 时,必须为默认可构造。(有一种方法可以绕过后面描述的此要求。
    get<your_type>()
    your_type
  • 在 函数 中,使用函数 at() 来访问对象值,而不是 。如果键不存在,则抛出可以处理的异常,但表现出未定义的行为。
    from_json
    operator[]
    at
    operator[]
  • 你不需要为 STL 类型添加序列化程序或反序列化程序,例如 :库已经实现了这些。
    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.get<T>(); // same as above, but with
                                  // adl_serializer<T>::from_json
            }
        }
    };
}

如何用于非默认的可构造/不可复制的类型?
get()

有一种方法,如果你的类型是移动可构造的。你还需要专门化,但具有特殊的过载:

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.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
  • 使用和何时需要 ADL
    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.get<TaskState>() == TS_RUNNING);

// undefined json value to enum (where the first map entry above is the default)
json jPi = 3.14;
assert(jPi.get<TaskState>() == TS_INVALID );

就像上面的任意类型转换一样,

  • NLOHMANN_JSON_SERIALIZE_ENUM()
    必须在枚举类型的命名空间(可以是全局命名空间)中声明,否则库将无法找到它,并且它将默认为整数序列化。
  • 它必须在你使用转换的任何地方都可用(例如,必须包含适当的标头)。

其他要点:

  • 使用 时,未定义的 JSON 值将默认为映射中指定的第一对。请仔细选择此默认对。
    get<ENUM_TYPE>()
  • 如果在映射中多次指定枚举或 JSON 值,则在与 JSON 转换时,将返回映射顶部的第一个匹配匹配项。

二进制格式(基本进制、中级银行同业拆放、消息包、二进制和 BJData)

虽然JSON是一种无处不在的数据格式,但它不是一种非常紧凑的格式,适合数据交换,例如通过网络。因此,该库支持 BSON(二进制 JSON)、CBOR(简明二进制对象表示)、消息包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(字节字符串)和消息包(箱、外接、修复)的二进制类型。默认情况下,它们存储在库外部进行处理。

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

支持的编译器

虽然已经是2022年了,但对C++11的支持仍然有点稀疏。目前,已知以下编译器可以工作:

  • GCC 4.8 - 12.0(可能更高版本)
  • 叮当声 3.4 - 15.0(可能更晚)
  • 苹果叮当声 9.1 - 13.1 (可能更晚)
  • 英特尔C++编译器 17.0.2(可能更高版本)
  • 英伟达 CUDA 编译器 11.0.221(可能更高版本)
  • 微软视觉C++ 2015 / 构建工具 14.0.25123.0 (可能更高版本)
  • 微软视觉C++ 2017 / 构建工具 15.5.180.51428 (可能更高版本)
  • 微软视觉C++ 2019 / 构建工具 16.3.1+1def00d3d(可能更高版本)
  • 微软可视化C++ 2022 / 构建工具 19.30.30709.0(可能更高版本)

我很乐意了解其他编译器/版本。

请注意:

  • 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
    

    该代码使用安卓NDK版本9 - 11(可能更高版本)和CrystaX的安卓NDK版本10成功编译。

  • 对于在 MinGW 或安卓 SDK 上运行的 GCC,可能会出现错误(或类似地,for 或 )。请注意,这不是代码的问题,而是编译器本身的问题。在 Android 上,请参阅上文以使用较新的环境进行构建。对于 MinGW,请参阅此站点此讨论,以获取有关如何修复此错误的信息。对于安卓NDK的使用,请参阅此讨论

    'to_string' is not a member of 'std'
    strtod
    strtof
    APP_STL := gnustl_static

  • 不受支持的 GCC 和 Clang 版本将被指令拒绝。这可以通过定义 来关闭。请注意,在这种情况下,你可以期望没有支持。

    #error
    JSON_SKIP_UNSUPPORTED_COMPILER_CHECK

以下编译器目前在 AppVeyor无人机 CIGitHub 操作的持续集成中使用:

编译器 操作系统 CI 提供商
苹果叮当声 11.0.3 (叮当声-1103.0.32.62);X代码 11.7 苹果操作系统 11.6.8 操作
苹果叮当声 12.0.0 (叮当声-1200.0.32.29);X代码 12.4 苹果操作系统 11.6.8 操作
苹果叮当声 12.0.5 (叮当声-1205.0.22.11);X代码 12.5.1 苹果操作系统 11.6.8 操作
苹果叮当声 13.0.0 (叮当声-1300.0.29.3);X代码 13.0 苹果操作系统 11.6.8 操作
苹果叮当声 13.0.0 (叮当声-1300.0.29.3);X代码 13.1 苹果操作系统 12.4 操作
苹果叮当声 13.0.0 (叮当声-1300.0.29.30);X代码 13.2.1 苹果操作系统 12.4 操作
苹果叮当声 13.1.6 (叮当声-1316.0.21.2.3);X代码 13.3.1 苹果操作系统 12.4 操作
苹果叮当声 13.1.6 (叮当声-1316.0.21.2.5);X代码 13.4.1 苹果操作系统 12.4 操作
叮叮当当 3.5.2 乌班图 20.04.3 LTS 操作
叮叮当当 3.6.2 乌班图 20.04.3 LTS 操作
叮叮当当 3.7.1 乌班图 20.04.3 LTS 操作
叮叮当当 3.8.1 乌班图 20.04.3 LTS 操作
叮叮当当 3.9.1 乌班图 20.04.3 LTS 操作
叮叮当当 4.0.1 乌班图 20.04.3 LTS 操作
叮叮当当 5.0.2 乌班图 20.04.3 LTS 操作
叮叮当当 6.0.1 乌班图 20.04.3 LTS 操作
叮叮当当 7.0.1 乌班图 20.04.3 LTS 操作
叮叮当当 8.0.0 乌班图 20.04.3 LTS 操作
叮叮当当 9.0.0 乌班图 20.04.3 LTS 操作
叮叮当当 10.0.0 乌班图 20.04.3 LTS 操作
使用类似 GNU 的命令行叮当 10.0.0 视窗-10.0.17763 操作
使用类似 GNU 的命令行叮当 11.0.0 视窗-10.0.17763 操作
叮当 11.0.0 与类似 MSVC 的命令行 视窗-10.0.17763 操作
叮叮当当 11.0.0 乌班图 20.04.3 LTS 操作
叮叮当当 12.0.0 乌班图 20.04.3 LTS 操作
叮叮当当 13.0.0 乌班图 20.04.3 LTS 操作
叮叮当当 14.0.0 乌班图 20.04.3 LTS 操作
Clang 15.0.0 (15.0.0-++20220719071818+5fc621355110-1exp120220719071918.324) 乌班图 20.04.3 LTS 操作
海湾合作委员会 4.8.5 (乌班图 4.8.5-4乌本图2) 乌班图 20.04.3 LTS 操作
海湾合作委员会 4.9.4 乌班图 20.04.3 LTS 操作
海湾合作委员会 5.5.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 6.5.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 7.5.0 乌班图 20.04.3 LTS 操作
GCC 8.1.0 (i686-posix-矮-rev0, 由 MinGW-W64 项目构建) 视窗-10.0.17763 操作
GCC 8.1.0 (x86_64-正交-她-rev0, 由 MinGW-W64 项目构建) 视窗-10.0.17763 操作
海湾合作委员会 8.5.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 9.5.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 10.4.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 11.1.0 乌班图(阿奇64) 无人机 CI
海湾合作委员会 11.3.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 12.2.0 乌班图 20.04.3 LTS 操作
海湾合作委员会 13.0.0 20220605(实验) 乌班图 20.04.3 LTS 操作
英特尔C++编译器 2021.5.0.20211109 乌班图 20.04.3 LTS 操作
内华达州 11.0.221 乌班图 20.04.3 LTS 操作
Visual Studio 14 2015 MSVC 19.0.24241.7 (生成引擎版本 14.0.25420.1) 视窗-6.3.9600 应用维约
Visual Studio 15 2017 MSVC 19.16.27035.0(生成引擎版本 15.9.21+g9802d43bc3 用于 .NET 框架) 视窗-10.0.14393 应用维约
Visual Studio 16 2019 MSVC 19.28.29912.0(用于 .NET 框架的内部生成引擎版本 16.9.0+57a23d249) 视窗-10.0.17763 操作
Visual Studio 16 2019 MSVC 19.28.29912.0(用于 .NET 框架的内部生成引擎版本 16.9.0+57a23d249) 视窗-10.0.17763 应用维约
Visual Studio 17 2022 MSVC 19.30.30709.0(用于 .NET 框架的生成引擎版本 17.0.31804.368) 视窗-10.0.20348 操作

集成

json.hpp 是此处发布或发布的单个必需文件。你需要添加

single_include/nlohmann

#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;

到要处理 JSON 的文件,并设置必要的开关以启用 C++11(例如,对于 GCC 和 Clang)。

-std=c++11

你可以进一步使用文件包含/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)
嵌入式(FetchContent)

从 CMake v3.11 开始,Fetch内容可用于在配置时自动下载版本作为依赖项。

例:

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和自制软件,只需键入即可。如果你想要出血边缘而不是最新版本,请使用 。有关详细信息,请参阅 nlohmann-json

brew install nlohmann-json
brew install nlohmann-json --HEAD

如果你使用的是介子构建系统,请将此源树添加为介子子项目。你还可以使用此项目的“发布”中的“发布”来减小供应商源代码树的大小。或者,你可以通过从介子 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

如果你使用柯南来管理你的依赖项,只需将 nlohmann_json/x.y.z 添加到你的需求中,你要使用的发行版本在哪里。如果你在使用软件包时遇到问题,请在此处提交问题。

conanfile
x.y.z

如果你使用 Spack 来管理依赖项,则可以使用 nlohmann-json 软件包。有关包装的任何问题,请参阅 spack 项目

如果你在项目中将 hunter 用于外部依赖项,则可以使用nlohmann_json包。有关包装的任何问题,请参阅猎人项目。

如果你使用的是巴卡鲁,则可以使用 安装此库的模块。请在此处提交问题。这里有一个演示存储库。

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,则可以通过将容器添加到容器文件来使用库(请参阅示例)。请在此处提交问题。

"nlohmann_json", '~>3.1.2'

如果使用的是 NuGet,则可以使用包 nlohmann.json。请查看有关如何使用该软件包的广泛说明。请在此处提交问题。

如果你使用的是 conda,则可以使用从 conda-forge 执行 nlohmann_json包。请在此处提交问题。

conda install -c conda-forge nlohmann_json

如果你使用的是 MSYS2,则可以使用明w-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开发”更改为现有标记或其他分支。

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

pkg-config nlohmann_json --cflags

Meson构建系统的用户还可以使用系统范围的库,该库将通过以下方式找到:

pkg-config

json = dependency('nlohmann_json', required: true)

许可证

该类根据 MIT 许可证进行许可

版权所有 © 2013-2022 尼尔斯·洛曼

特此免费授予获得本软件和相关文档文件(“软件”)副本的任何人不受限制地处理本软件的许可,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,并允许向其提供本软件的人员这样做, 须符合以下条件:

上述版权声明和本许可声明应包含在本软件的所有副本或大部分内容中。

本软件按“原样”提供,不作任何明示或暗示的担保,包括但不限于适销性、特定用途适用性和不侵权的担保。在任何情况下,作者或版权所有者都不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为或其他方面,由本软件或本软件的使用或其他交易引起、产生或与之相关。


该类包含来自比约恩·霍尔曼的 UTF-8 解码器,该解码器根据 MIT 许可证获得许可(见上文)。版权所有 © 2008-2009 比约恩·霍尔曼 bjoern@hoehrmann.de

该类包含来自弗洛里安·洛伊奇的Grisu2算法的略微修改版本,该版本根据MIT许可证获得许可(见上文)。版权所有 © 2009 弗洛里安·洛伊奇

该类包含埃文·内默森的赫德利副本,该副本被许可为CC0-1.0

该类包含谷歌Abseil的部分内容,这些部分内容是根据Apache 2.0许可证许可的

联系

如果你对库有疑问,我想邀请你在GitHub上提出一个问题。请尽可能详细地描述你的请求、问题或疑问,并提及你正在使用的库的版本以及编译器和操作系统的版本。在 GitHub 上打开问题允许此库的其他用户和贡献者进行协作。例如,我对MSVC的经验很少,这方面的大多数问题都由一个不断发展的社区解决了。如果你查看已关闭的问题,你会发现在大多数情况下我们 React 非常及时。

仅当你的请求包含机密信息时,请向我发送电子邮件。对于加密邮件,请使用此密钥

安全

尼尔斯·洛曼的提交版本都使用此PGP密钥进行签名。

谢谢

我非常感谢以下人员的帮助。

  1. 腾必乐实现了CMake支持和lcov集成,在字符串解析器中实现了转义和Unicode处理,并修复了JSON序列化。
  2. 艾略特古德里奇修复了迭代器类中双重删除的问题。
  3. kirkshoop 使该类的迭代器可组合到其他库。
  4. wancw 修复了一个阻碍类使用 Clang 进行编译的错误。
  5. 托马斯·阿布拉德在迭代器实现中发现了一个错误。
  6. 约书亚·兰德尔修复了浮点序列化中的一个错误。
  7. 亚伦·布格哈特实现了以增量方式解析流的代码。此外,他允许过滤器函数的定义在解析时丢弃不需要的元素,从而大大改进了解析器类。
  8. 丹尼尔·科佩切克修复了GCC 5.0编译中的一个错误。
  9. 弗洛里安·韦伯修复了一个错误,并改进了比较运算符的性能。
  10. 埃里克·科尼利厄斯指出了处理NaN和无穷大值时的一个错误。他还改进了字符串转义的性能。
  11. 易思龙实现了从匿名枚举的转换。
  12. 凯普金耐心地推动了对微软视觉工作室的支持。
  13. gregmarr简化了反向迭代器的实现,并帮助了许多提示和改进。特别是,他推动了用户定义类型的实现。
  14. 卡约·卢皮修复了统一码处理中的一个错误。
  15. 达里奥姆特修复了示例中的一些拼写错误。
  16. 丹尼尔·弗雷清理了一些指针并实现了异常安全的内存分配。
  17. 科林·赫希处理了一个小的命名空间问题。
  18. 胡阮更正了文档中的变量名称。
  19. 银草重载以接受右值引用。
    parse()
  20. dariomt 修复了 MSVC 类型支持中的一个微妙之处,并实现了该函数来获取对存储值的引用。
    get_ref()
  21. 扎尔格拉夫添加了一个允许使用安卓NDK进行编译的解决方法。
  22. “咔嚓咔咔”取代了被视觉工作室标记为不安全的功能。
  23. 406345修复了两个小警告。
  24. 格伦·费尔南德斯指出了该函数中潜在的可移植性问题。
    has_mapped_type
  25. 科尔宾·休斯修复了贡献指南中的一些拼写错误。
  26. twelsby 修复了数组下标运算符、MSVC 生成失败的问题以及浮点分析/转储。他进一步添加了对无符号整数的支持,并为解析的数字实现了更好的往返支持。
  27. 沃尔克·迪尔斯-格拉布施修复了自述文件中的一个链接。
  28. msm- 添加了对美国模糊 Lop 的支持。
  29. 安妮希尔修复了自述文件中的一个示例。
  30. 主题在自述文件中注意到一个错误的网址。
  31. 吕正修复了 和 的命名空间问题。
    int64_t
    uint64_t
  32. abc100m分析了GCC 4.8的问题,并提出了一个部分解决方案
  33. zewt 在自述文件中添加了有关安卓的有用注释。
  34. 罗伯特·马尔基添加了一个使用移动迭代器的修复,并通过CMake改进了集成。
  35. 克里斯·基钦清理了CMake文件。
  36. 汤姆·李约瑟修复了MSVC 2015的一个微妙错误,这也是迈克尔K.提出的。
  37. 马里奥·费罗尔迪修正了一个小错别字。
  38. 邓肯维尔纳在2.0.0版本中发现了一个非常令人尴尬的性能倒退。
  39. 达米安修复了最后的转换警告之一。
  40. 托马斯·布劳恩修复了测试用例中的警告,并在 CI 中调整了 MSVC 调用。
  41. Théo DELRIEU耐心而富有建设性地监督了迭代器范围解析的漫长道路。他还实现了用户定义类型的序列化/反序列化背后的魔力,并将单个头文件拆分为更小的块。
  42. Stefan 修复了文档中的一个小问题。
  43. 瓦西里·迪莫夫修复了有关从转换的文档。
    std::multiset
  44. 克里斯托夫朱德过度劳累了CMake文件,以简化项目纳入。
  45. 弗拉基米尔·彼得里戈使SFINAE黑客更具可读性,并将视觉工作室17添加到构建矩阵中。
  46. 丹尼斯·安德烈朱修复了自述文件中的语法问题。
  47. 皮埃尔-安托万·拉卡兹在函数中发现了一个微妙的错误。
    dump()
  48. TurpentineDistillery 指出 std::locale::classic() 以避免过多的区域设置慢跑,在解析器中发现了一些不错的性能改进,改进了基准测试代码,并实现了与区域设置无关的数字解析和打印。
  49. cgzones有一个想法,如何修复覆盖性扫描。
  50. 贾里德·格拉布对一个令人讨厌的文档警告保持沉默。
  51. 张毅新修复了整数溢出检查。
  52. 博斯维斯特法伦将两个迭代器类合并为一个更小的迭代器类。
  53. 丹尼尔599帮助特拉维斯用克朗的消毒剂执行测试。
  54. 乔纳森·李修复了自述文件中的一个示例。
  55. gnzlbg 支持用户定义类型的实现。
  56. Alexej Harm 帮助用户定义的类型与可视化工作室配合使用。
  57. 贾里德·格拉布支持用户定义类型的实现。
  58. 恩里科比拉在一个例子中指出了一个错别字。
  59. 马丁·霍热诺夫斯基找到了一种方法,可以在测试套件的编译时间内将速度提高2倍。
  60. ukhegg发现对示例部分提出了改进建议。
  61. 斯万森-伊希在自述文件中指出了一个错别字。
  62. 米哈伊斯坦修复了与s比较中的一个错误。
    nullptr
  63. 图沙尔·马赫什瓦里增加了对共音的支持,以加快编译速度。
  64. TedLyngmo在自述文件中注意到一个拼写错误,删除了不必要的位算术,并修复了一些警告。
    -Weffc++
  65. 克日什托夫·沃希使例外更加明显。
  66. ftillier 修复了编译器警告。
  67. 锡卷确保所有推送的警告都正确弹出。
  68. Fytch 在文档中发现了一个错误。
  69. 杰伊·西斯塔实现了介子构建描述。
  70. 亨利·李在ICC中修复了一个警告,并改进了迭代器的实现。
  71. 文森特·蒂耶里为柯南软件包管理器维护一个软件包。
  72. 斯蒂芬修复了 MSVC 和 的潜在问题。
    std::min
  73. 迈克·祖修复了一些错别字。
  74. amrcode指出了一个关于浮点数比较的误导性文档。
  75. 远藤奥列格通过将 替换为 来减少内存消耗。
    <iostream>
    <iosfwd>
  76. dan-42 清理了 CMake 文件,以简化库的包含/重用。
  77. 尼基塔·奥菲采罗夫允许从初始值设定项列表中移动值。
  78. 格雷格·赫雷尔修正了一个错别字。
  79. 德米特里·库科维涅茨修复了一个错别字。
  80. kbthomp1 修复了与英特尔 OSX 编译器相关的问题。
  81. 马库斯·韦尔修复了一个错别字。
  82. 修复了前提条件检查中的一个细微错误。
  83. Alex 在代码示例中注意到一个错误。
  84. 汤姆·德·格乌斯向国际刑事法院报告了一些警告,并帮助解决了这些问题。
  85. 佩里·昆德特简化了对输入流的读取。
  86. 索努·洛哈尼修复了一个小的编译错误。
  87. 杰米·苏厄德修复了所有MSVC警告。
  88. 内特·巴尔加斯添加了一个多克西根标签文件。
  89. pvleuven帮助修复了ICC中的警告。
  90. 帕维尔帮助修复了MSVC中的一些警告。
  91. 杰米·苏厄德避免了和中不必要的字符串副本。
    find()
    count()
  92. 米蒂亚修复了一些错别字。
  93. 乔里特·弗龙斯基更新了亨特软件包链接。
  94. 马蒂亚斯·穆勒为 MSVC 调试视图添加了一个。
    .natvis
  95. bogemic 修复了一些C++17 的弃用警告。
  96. 埃伦·奥卡修复了一些 MSVC 警告。
  97. abolz集成了Grisu2算法,以实现正确的浮点格式,允许更多的往返检查成功。
  98. 瓦迪姆·埃瓦德修复了自述文件中的一个标记问题。
  99. 零缺陷修复了编译器警告。
  100. Kert 允许在序列化中对字符串类型进行模板化,并增加了重写异常行为的可能性。
  101. 马克-99帮助修复了ICC错误。
  102. 帕特里克·胡贝尔修复了自述文件中的链接。
  103. johnfb 在实现 CBOR 的不定长度字符串时发现了一个错误。
  104. 保罗·富尔茨二世在 cget 包管理器上添加了注释。
  105. 林伟信使自述文件的整合部分更加简洁。
  106. 拉尔夫比利格检测到并修复了解析器回调中的内存泄漏。
  107. 允许将 JSON 转储为备用字符串类型。
  108. 凯文·托农在 CMake 中过度使用了C++11 编译器检查。
  109. 阿克塞尔·休布尔简化了 CMake 检查,并添加了对 Spack 包管理器的支持
  110. 卡洛斯·奥瑞安修正了一个错别字。
  111. 詹姆斯·厄普约翰在编译器部分修复了一个版本号。
  112. 查克·阿特金斯根据 CMake 打包指南调整了 CMake 文件,并为 CMake 集成提供了文档。
  113. 扬·舍帕赫修正了一个错别字。
  114. 马丁制造修复了一个错别字。
  115. 马蒂亚斯·穆勒从 中删除了依赖关系。
    std::stringstream
  116. agrianius 添加了代码以使用替代字符串实现。
  117. Daniel599允许在该函数中使用更多算法。
    items()
  118. 朱利叶斯·拉科修复了介子包含目录,并修复了 cppreference.com 的链接。
  119. 索努·洛哈尼在调试模式下修复了 MSVC 2015 的编译。
  120. grembo 修复了测试套件并重新启用了多个测试用例。
  121. Hyeon Kim 引入了宏来控制库内的异常处理。
    JSON_INTERNAL_CATCH
  122. 修复了编译器警告。
  123. 大卫·格思里修复了Clang 3.4.2的一个微妙的编译错误。
  124. 丹尼斯·费舍尔允许在不安装图书馆的情况下打电话。
    find_package
  125. 金贤修复了双重宏观定义的问题。
  126. 本·伯曼使一些错误消息更易于理解。
  127. zakalibit 修复了英特尔C++编译器的编译问题。
  128. 曼德雷尔修复了一个编译问题。
  129. 科斯蒂安廷·波诺马连科向介子构建文件添加了版本和许可证信息。
  130. 亨利·施莱纳增加了对GCC 4.8的支持。
  131. knilch 确保测试套件在错误的目录中运行时不会停止。
  132. 安东尼奥·博伦多修复了MSVC 2017年的警告。
  133. 丹·根德罗实现了该宏以快速定义枚举/JSON 映射。
    NLOHMANN_JSON_SERIALIZE_ENUM
  134. efp 添加了行和列信息来解析错误。
  135. 朱利安-贝克尔添加了对BSON的支持。
  136. 普拉蒂克·乔杜里添加了对结构化绑定的支持。
  137. 大卫·阿维迪西安增加了对克朗5.0.1(PS4版本)的支持。
  138. 乔纳森·杜马雷克实现了一个输入适配器来读取。
    FILE*
  139. kjpus 修复了文档中的一个链接。
  140. 曼文德拉·辛格修复了文档中的一个错别字。
  141. 兹古拉特29修复了一个MSVC警告。
  142. 西尔万·科莱添加了代码以避免MSVC出现问题。
  143. 修复了从输入流解析 JSON 时的一个错误。
  144. 米利安·波奎特允许通过介子安装图书馆。
  145. 迈克尔·贝恩斯-米勒发现了一个缺少命名空间的问题。
  146. 纳什塔诺维奇·费伦茨修复了 libc 2.12 的编译问题。
  147. 安德烈亚斯·施瓦布修复了字节序转换。
  148. 马克-邓宁修复了 MSVC 中的一个警告。
  149. 加雷斯·西尔维斯特-布拉德利为JSON指针添加。
    operator/
  150. 约翰-马克注意到一个缺失的标题。
  151. 维塔利·扎伊采夫修复了GCC 9.0的编译。
  152. 洛朗·斯塔库尔修复了与GCC 9.0的编译。
  153. 艾弗·万德斯帮助将CMake要求降低到3.1版本。
  154. njlr更新了巴卡鲁指令。
  155. 狮子修复了 CentOS 上 GCC 7 的编译问题。
  156. 艾萨克·尼克因改进了整数序列化性能并实现了该函数。
    contains()
  157. 逾期未付抑制了无法修复的警告。
  158. 埃尔维斯·奥里克改进了介子支持。
  159. 马杰伊·普赫在自述文件中修复了一个例子。
  160. 马克·贝克维斯修正了一个错别字。
  161. 修复了序列化程序中的错误。
  162. 帕特里克·博彻实现了JSON指针。
    push_back()
    pop_back()
  163. 布鲁诺·奥利维拉增加了对康达的支持。
  164. 米歇尔·凯尼修复了自述文件中的链接。
  165. 哈尼记录了如何使用 NuGet 安装库。
  166. 马克·贝克维斯修正了一个错别字。
  167. 扬-莫林-1998帮助将CMake要求降低到3.1版。
  168. 康斯坦丁·波兹维罗夫维护着一个 MSYS2 软件发行版的软件包。
  169. 雷米亚贝尔将 GNU安装对象添加到了 CMake 文件中。
  170. 泰勒·霍华德修复了一个单元测试。
  171. 加布·罗恩实现了这种方法。
    to_string
  172. 岩崎和田修正了一个叮当声警告。
  173. 维克多·基里洛夫将单元测试从捕获切换到文档测试
  174. 俊成鄂修了一个错别字。
  175. tete17 修复了函数中的一个错误。
    contains
  176. Xav83 修复了一些 cpp 检查警告。
  177. 0x路透了修复一些错别字。
  178. 克里斯蒂安·德内克添加了一个康斯特版本的.
    json_pointer::back
  179. 朱利安·哈迈德使该函数适用于自定义字符串类型。
    items()
  180. Evan Nemerson updated fixed a bug in Hedley and updated this library accordingly.
  181. Florian Pigorsch fixed a lot of typos.
  182. Camille Bégué fixed an issue in the conversion from and to .
    std::pair
    std::tuple
    json
  183. Anthony VH fixed a compile error in an enum deserialization.
  184. Yuriy Vountesmery noted a subtle bug in a preprocessor check.
  185. Chen fixed numerous issues in the library.
  186. Antony Kellermann added a CI step for GCC 10.1.
  187. Alex fixed an MSVC warning.
  188. Rainer proposed an improvement in the floating-point serialization in CBOR.
  189. Francois Chabot made performance improvements in the input adapters.
  190. Arthur Sonzogni documented how the library can be included via .
    FetchContent
  191. Rimas Misevičius fixed an error message.
  192. Alexander Myasnikov fixed some examples and a link in the README.
  193. Hubert Chathi made CMake's version config file architecture-independent.
  194. OmnipotentEntity implemented the binary values for CBOR, MessagePack, BSON, and UBJSON.
  195. ArtemSarmini fixed a compilation issue with GCC 10 and fixed a leak.
  196. Evgenii Sopov integrated the library to the wsjcpp package manager.
  197. Sergey Linev fixed a compiler warning.
  198. Miguel Magalhães fixed the year in the copyright.
  199. Gareth Sylvester-Bradley fixed a compilation issue with MSVC.
  200. Alexander “weej” Jones fixed an example in the README.
  201. Antoine Cœur fixed some typos in the documentation.
  202. jothepro updated links to the Hunter package.
  203. Dave Lee fixed link in the README.
  204. Joël Lamotte added instruction for using Build2's package manager.
  205. Paul Jurczak fixed an example in the README.
  206. Sonu Lohani fixed a warning.
  207. Carlos Gomes Martinho updated the Conan package source.
  208. Konstantin Podsvirov fixed the MSYS2 package documentation.
  209. Tridacnid improved the CMake tests.
  210. Michael fixed MSVC warnings.
  211. Quentin Barbarat fixed an example in the documentation.
  212. XyFreak fixed a compiler warning.
  213. TotalCaesar659 fixed links in the README.
  214. Tanuj Garg improved the fuzzer coverage for UBSAN input.
  215. AODQ fixed a compiler warning.
  216. jwittbrodt made inline.
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
  217. pfeatherstone improved the upper bound of arguments of the / macros.
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
    NLOHMANN_DEFINE_TYPE_INTRUSIVE
  218. Jan Procházka fixed a bug in the CBOR parser for binary and string values.
  219. T0b1-iOS fixed a bug in the new hash implementation.
  220. Matthew Bauer adjusted the CBOR writer to create tags for binary subtypes.
  221. gatopeich implemented an ordered map container for .
    nlohmann::ordered_json
  222. Érico Nogueira Rolim added support for pkg-config.
  223. KonanM proposed an implementation for the / macros.
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
    NLOHMANN_DEFINE_TYPE_INTRUSIVE
  224. Guillaume Racicot implemented support and allowed C++20 support.
    string_view
  225. Alex Reinking improved CMake support for .
    FetchContent
  226. Hannes Domani provided a GDB pretty printer.
  227. Lars Wirzenius reviewed the README file.
  228. Jun Jie fixed a compiler path in the CMake scripts.
  229. Ronak Buch fixed typos in the documentation.
  230. Alexander Karzhenkov fixed a move constructor and the Travis builds.
  231. Leonardo Lima added CPM.Cmake support.
  232. Joseph Blackman fixed a warning.
  233. Yaroslav updated doctest and implemented unit tests.
  234. Martin Stump fixed a bug in the CMake files.
  235. Jaakko Moisio fixed a bug in the input adapters.
  236. bl-ue fixed some Markdown issues in the README file.
  237. William A. Wieselquist fixed an example from the README.
  238. abbaswasim fixed an example from the README.
  239. Remy Jette fixed a warning.
  240. Fraser fixed the documentation.
  241. Ben Beasley updated doctest.
  242. Doron Behar fixed pkg-config.pc.
  243. raduteo fixed a warning.
  244. David Pfahler added the possibility to compile the library without I/O support.
  245. Morten Fyhn Amundsen fixed a typo.
  246. jpl-mac allowed to treat the library as a system header in CMake.
  247. Jason Dsouza fixed the indentation of the CMake file.
  248. offa added a link to Conan Center to the documentation.
  249. TotalCaesar659 updated the links in the documentation to use HTTPS.
  250. Rafail Giavrimis fixed the Google Benchmark default branch.
  251. Louis Dionne fixed a conversion operator.
  252. justanotheranonymoususer made the examples in the README more consistent.
  253. Finkman suppressed some warnings.
    -Wfloat-equal
  254. Ferry Huberts fixed warnings.
    -Wswitch-enum
  255. Arseniy Terekhin made the GDB pretty-printer robust against unset variable names.
  256. Amir Masoud Abdol updated the Homebrew command as nlohmann/json is now in homebrew-core.
  257. Hallot fixed some .
    -Wextra-semi-stmt warnings
  258. Giovanni Cerretani fixed warnings on .
    -Wunused
    JSON_DIAGNOSTICS
  259. Bogdan Popescu hosts the docset for offline documentation viewers.
  260. Carl Smedstad fixed an assertion error when using .
    JSON_DIAGNOSTICS
  261. miikka75 provided an important fix to compile C++17 code with Clang 9.
  262. Maarten Becker fixed a warning for shadowed variables.
  263. Cristi Vîjdea fixed typos in the documentation.
    operator[]
  264. Alex Beregszaszi fixed spelling mistakes in comments.
  265. Dirk Stolle fixed typos in documentation.
  266. Daniel Albuschat corrected the parameter name in the documentation.
    parse
  267. Prince Mendiratta fixed a link to the FAQ.
  268. Florian Albrechtskirchinger implemented support for object keys and made dozens of other improvements.
    std::string_view
  269. Qianqian Fang implemented the Binary JData (BJData) format.
  270. pketelsen added macros and .
    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
  271. DarkZeros adjusted to code to not clash with Arduino defines.
  272. flagarde fixed the output of for MSVC.
    meta()
  273. Giovanni Cerretani fixed a check for .
    std::filesystem
  274. Dimitris Apostolou fixed a typo.
  275. Ferry Huberts fixed a typo.
  276. Michael Nosthoff fixed a typo.
  277. JungHoon Lee fixed a typo.
  278. Faruk D. fixed the CITATION.CFF file.
  279. Andrea Cocito added a clarification on macro usage to the documentation.
  280. Krzysiek Karbowiak refactored the tests to use .
    CHECK_THROWS_WITH_AS
  281. Chaoqi Zhang fixed a typo.
  282. ivanovmp fixed a whitespace error.
  283. KsaNL fixed a build error when including .
    <windows.h>
  284. Andrea Pappacoda moved and files to directory.
    .pc
    .cmake
    share
  285. Wolf Vollprecht added the function.
    patch_inplace
  286. Jake Zimmerman highlighted common usage patterns in the README file.
  287. NN added the Visual Studio output directory to .
    .gitignore
  288. Romain Reignier improved the performance the vector output adapter.
  289. Mike fixed the .
    std::iterator_traits
  290. Richard Hozák added macro to disable default enum conversions.
    JSON_NO_ENUM
  291. vakokako fixed tests when compiling with C++20.
  292. Alexander “weej” Jones fixed an example in the README.
  293. Eli Schwartz added more files to the archive.
    include.zip
  294. Kevin Lu fixed a compilation issue when typedefs with certain names were present.
  295. Trevor Hickey improved the description of an example.
  296. Jef LeCompte updated the year in the README file.
  297. Alexandre Hamez fixed a warning.
  298. Maninderpal Badhan fixed a typo.
  299. kevin-- added a note to an example in the README file.
  300. I fixed a typo.
  301. Gregorio Litenstein fixed the Clang detection.
  302. Andreas Smas added a Doozer badge.
  303. WanCW fixed the string conversion with Clang.
  304. zhaohuaxishi fixed a Doxygen error.
  305. emvivre removed an invalid parameter from CMake.
  306. Tobias Hermann fixed a link in the README file.
  307. Michael fixed a warning.
  308. Ryan Mulder added to the function.
    ensure_ascii
    dump
  309. Muri Nicanor fixed the discovery in the Makefile.
    sed
  310. David Avedissian implemented SFINAE-friendly .
    iterator_traits
  311. AQNOUCH Mohammed fixed a typo in the README.
  312. Gareth Sylvester-Bradley added and to construct JSON pointers.
    operator/=
    operator/
  313. Michael Macnair added support for afl-fuzz testing.
  314. Berkus Decker fixed a typo in the README.
  315. Illia Polishchuk improved the CMake testing.
  316. Ikko Ashimine fixed a typo.

Thanks a lot for helping out! Please let me know if I forgot someone.

Used third-party tools

The library itself consists of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot!

Projects using JSON for Modern C++

The library is currently used in Apple macOS Sierra-Monterey and iOS 10-15. I am not sure what they are using the library for, but I am happy that it runs on so many devices.

Notes

Character encoding

The library supports Unicode input as follows:

  • Only UTF-8 encoded input is supported which is the default encoding for JSON according to RFC 8259.
  • std::u16string
    and can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers.
    std::u32string
  • Other encodings such as Latin-1 or ISO 8859-1 are not supported and will yield parse or serialization errors.
  • Unicode noncharacters will not be replaced by the library.
  • Invalid surrogates (e.g., incomplete pairs such as ) will yield parse errors.
    \uDEAD
  • The strings stored in the library are UTF-8 encoded. When using the default string type (), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
    std::string
  • When you store strings with different encodings in the library, calling
    dump()
    may throw an exception unless or are used as error handlers.
    json::error_handler_t::replace
    json::error_handler_t::ignore
  • To store wide strings (e.g., ), you need to convert them to a UTF-8 encoded before, see an example.
    std::wstring
    std::string

Comments in JSON

This library does not support comments by default. It does so for three reasons:

  1. Comments are not part of the JSON specification. You may argue that or are allowed in JavaScript, but JSON is not JavaScript.

    //
    /* */

  2. This was not an oversight: Douglas Crockford wrote on this in May 2012:

    I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't.

    Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser.

  3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check The Harmful Consequences of the Robustness Principle on this.

However, you can pass set parameter to true in the function to ignore or comments. Comments will then be treated as whitespace.

ignore_comments
parse
//
/* */

Order of object keys

By default, the library does not preserve the insertion order of object elements. This is standards-compliant, as the JSON standard defines objects as "an unordered collection of zero or more name/value pairs".

If you do want to preserve the insertion order, you can try the type

nlohmann::ordered_json
. Alternatively, you can use a more sophisticated ordered map like
tsl::ordered_map
(integration) or
nlohmann::fifo_map
(integration).

Memory Release

We checked with Valgrind and the Address Sanitizer (ASAN) that there are no memory leaks.

If you find that a parsing program with this library does not release memory, please consider the following case, and it may be unrelated to this library.

Your program is compiled with glibc. There is a tunable threshold that glibc uses to decide whether to actually return memory to the system or whether to cache it for later reuse. If in your program you make lots of small allocations and those small allocations are not a contiguous block and are presumably below the threshold, then they will not get returned to the OS. Here is a related issue #1924.

Further notes

  • 该代码包含许多调试断言,可以通过定义预处理器宏来关闭这些断言,请参阅断言的文档。特别是,note 运算符[] 实现对 const 对象的未经检查的访问:如果给定的键不存在,则行为未定义(想想取消引用的空指针),如果打开断言,则产生断言失败。如果不确定对象中的元素是否存在,请将检查访问权限与 at() 函数结合使用。此外,还可以 定义 以替换对 的调用。
    NDEBUG
    JSON_ASSERT(x)
    assert(x)
  • 由于 JSON 规范中未定义确切的数字类型,因此此库会尝试自动选择最合适的数字类型C++。因此,如果调用代码中未屏蔽浮点异常,则该类型可用于存储在某些极少数情况下可能产生浮点异常的数字。这些异常不是由库引起的,需要在调用代码中修复,例如在调用库函数之前重新屏蔽异常。
    double
  • 代码可以在不C++运行时类型识别功能的情况下进行编译;也就是说,你可以使用编译器标志。
    -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:精确