Using the nlohmann Open-Source JSON Library in C++: A Practical Guide
Introduction
In the past, JSON processing in C++ was often handled using Qt's QJsonDocument and associated classess. However, this approach tends to be verbose and less elegant. For a more convenient and intuitive solution, many developers now rely on the open-source libray nlohmann/json. Highly regarded worldwide, it offers concise methods for working with JSON data in C++ and has become the library of choice for numerous programmers.
Rather than referencing the exhaustive documentation available on its GitHub page, which can sometimes feel overwhelming and include seldom-used features, this article aims to sumarize the essential functionalities alongside examples, enabling new users to grasp the library quickly.
Project Setup
Although nlohmann/json includes CMake configuration files, you don't need to go through the CMake setup process for basic usage. Simply copy the contents of the include directory and add it to your project. With the header file included, you can start using the library as follows:
#include "nlohmann/json.hpp"
using json = nlohmann::json;
Core Features
1. Creating JSON Objects
You can construct JSON objects conveniently by assigning values to named keys:
json data;
data["pi"] = 3.1415;
data["happy"] = true;
data["name"] = "Niels";
data["nothing"] = nullptr;
data["nested_object"]["key"] = "value";
data["array"] = {1, 2, 3};
data["complex_obj"] = { {"currency", "USD"}, {"value", 99.99} };
Explicit type initialization is also available when needed:
json emptyArray = json::array();
json emptyObj = json::object();
json arrayWithData = json::array({ {"currency", "USD"}, {"value", 99.99} });
2. Creating JSON from STL Containers
nlohmann seamlessly integrates with standard STL containers:
std::vector<int> intVec = {1, 2, 3, 4};
json vecJson(intVec);
std::map<std::string, int> strIntMap = { {"one", 1}, {"two", 2} };
json mapJson(strIntMap);
Containers such as std::set, std::unordered_map, and std::array are also supported, preserving their corresponding behavior.
3. Serializing and Deserializing JSON
- Deserialization: Convert a string directly into a JSON object.
json deserialized = "{ \"hello\": true }"_json;
- Serialization: Convert a JSON object into a string.
std::string serialized = deserialized.dump();
std::cout << deserialized.dump(4) << std::endl; // Pretty formatting
4. File and Stream Usage
You can intuitively read from or write to local files:
// Deserialize from file
std::ifstream input("data.json");
json fileJson;
input >> fileJson;
// Serialize to file
std::ofstream output("formatted.json");
output << std::setw(4) << fileJson << std::endl;
5. Mapping JSON to Custom Data Types
You can convert between JSON objects and custom C++ structs by defining to_json and from_json functions:
struct Person {
std::string name;
int age;
};
void to_json(json& j, const Person& p) {
j = json{{"name", p.name}, {"age", p.age}};
}
void from_json(const json& j, Person& p) {
j.at("name").get_to(p.name);
j.at("age").get_to(p.age);
}
// Usage
Person person = {"Alice", 30};
json jsonData = person;
Person extracted = jsonData.get<Person>();
6. Binary Conversion
nlohmann supports serialization into compact binary formats such as CBOR and MessagePack:
json sample = { {"compact", true}, {"version", 1} };
std::vector<uint8_t> cborData = json::to_cbor(sample);
To save binary-encoded data into a file:
std::ofstream output("binary.dat", std::ios::binary);
output.write(reinterpret_cast<const char*>(cborData.data()), cborData.size());
Practical Examples
Example 1: Parsing Simple JSON
#include "nlohmann/json.hpp"
#include <fstream>
using json = nlohmann::json;
int main() {
std::ifstream input("simple.json");
json j;
input >> j;
float pi = j.at("pi");
bool happy = j.at("happy");
return 0;
}
Example 2: Nested JSON Parsing
For a complex JSON structure with nested objects and arrays, define structs and write helper funcsions for parsing:
#include "nlohmann/json.hpp"
#include <fstream>
#include <string>
struct Track {
std::string name;
std::vector<std::string> files;
};
struct VideoSettings {
int width;
int height;
};
void to_json(json& j, const Track& t) {
j = json{{"name", t.name}, {"files", t.files}};
}
int main() {
std::ifstream input("complex.json");
json parsedData;
input >> parsedData;
VideoSettings settings = parsedData["settings"].get<VideoSettings>();
return 0;
}