nlohmann/json (Validation)
Library
nlohmann/json (Validation)
Overview
nlohmann/json is the most popular JSON library for Modern C++. It provides an intuitive API similar to the standard library, making JSON reading and writing straightforward. Validation functionality is achieved through powerful JSON schema validation by combining with third-party libraries (such as json-schema-validator or valijson). It supports C++11 and later, can be easily integrated as a header-only library, and allows handling JSON data with the same feel as STL containers.
Details
The nlohmann/json library is a modern C++ library for parsing, generating, and manipulating JSON. It provides an intuitive API similar to STL containers, allowing JSON objects to be handled naturally as C++ values. It supports basic type checking, structural validation, and implementation of custom validation logic as validation features. Through integration with external libraries, JSON Schema Draft 4/7 compliant schema validation is also possible. It offers an excellent balance of performance and usability and is widely adopted in enterprise-level production environments.
Key Features
- Intuitive API: Natural operation feel like STL containers
- Type Safety: Strict type checking and error handling
- Schema Validation: Advanced validation through combination with external libraries
- High Performance: Optimized JSON parsing and generation engine
- Header-only: Easy integration and distribution
- Rich Features: Custom serializers, iterators, patch operations, and more
Pros and Cons
Pros
- Intuitive and easy-to-use API similar to C++ standard library
- Excellent performance and efficient memory usage
- Rich documentation and active community support
- Proven track record with adoption by many companies
- Flexible customization features and extensibility
- Comprehensive error handling and debugging support
Cons
- Increased compilation time (large header file)
- Built-in schema validation functionality is limited
- External libraries required for complex validation
- High memory usage when processing large JSON files
- Learning curve exists (especially for advanced features)
References
Code Examples
Installation and Basic Setup
# Install using vcpkg
vcpkg install nlohmann-json
# Install using Conan
conan install nlohmann_json/3.11.2@
# CMake find_package usage example
find_package(nlohmann_json REQUIRED)
target_link_libraries(your_target nlohmann_json::nlohmann_json)
# Without package manager
wget https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp
# Place json.hpp in appropriate directory
Basic JSON Operations and Validation
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
using json = nlohmann::json;
// Basic validation functionality
class JSONValidator {
public:
// JSON type checking
static bool isString(const json& j) {
return j.is_string();
}
static bool isNumber(const json& j) {
return j.is_number();
}
static bool isBoolean(const json& j) {
return j.is_boolean();
}
static bool isArray(const json& j) {
return j.is_array();
}
static bool isObject(const json& j) {
return j.is_object();
}
// Check required fields
static bool hasRequiredFields(const json& j, const std::vector<std::string>& fields) {
if (!j.is_object()) {
return false;
}
for (const auto& field : fields) {
if (!j.contains(field)) {
return false;
}
}
return true;
}
// Value range check
static bool isInRange(const json& j, double min_val, double max_val) {
if (!j.is_number()) {
return false;
}
double value = j.get<double>();
return value >= min_val && value <= max_val;
}
// String length check
static bool isStringLengthValid(const json& j, size_t min_len, size_t max_len) {
if (!j.is_string()) {
return false;
}
std::string str = j.get<std::string>();
return str.length() >= min_len && str.length() <= max_len;
}
// Array size check
static bool isArraySizeValid(const json& j, size_t min_size, size_t max_size) {
if (!j.is_array()) {
return false;
}
return j.size() >= min_size && j.size() <= max_size;
}
};
int main() {
try {
// Basic JSON creation and validation
{
json person = {
{"name", "John Doe"},
{"age", 30},
{"email", "[email protected]"},
{"skills", {"C++", "Python", "JavaScript"}},
{"active", true}
};
std::cout << "Person JSON:\n" << person.dump(4) << std::endl;
// Basic validation
if (JSONValidator::hasRequiredFields(person, {"name", "age", "email"})) {
std::cout << "✓ All required fields present" << std::endl;
}
if (JSONValidator::isStringLengthValid(person["name"], 2, 50)) {
std::cout << "✓ Name length is valid" << std::endl;
}
if (JSONValidator::isInRange(person["age"], 0, 120)) {
std::cout << "✓ Age is within valid range" << std::endl;
}
if (JSONValidator::isArraySizeValid(person["skills"], 1, 10)) {
std::cout << "✓ Skills array size is valid" << std::endl;
}
}
// JSON file loading and validation
{
// Read JSON file
std::ifstream config_file("config.json");
if (config_file.is_open()) {
json config;
config_file >> config;
// Configuration file validation
std::vector<std::string> required_config = {"app_name", "port", "debug_mode"};
if (JSONValidator::hasRequiredFields(config, required_config)) {
std::cout << "✓ Configuration file is valid" << std::endl;
} else {
std::cout << "✗ Configuration file missing required fields" << std::endl;
}
config_file.close();
}
}
// Error handling example
{
json invalid_json = {
{"name", 123}, // Number instead of expected string
{"age", "thirty"}, // String instead of expected number
{"email", ""} // Empty string
};
// Type checking
if (!JSONValidator::isString(invalid_json["name"])) {
std::cout << "✗ Name must be a string" << std::endl;
}
if (!JSONValidator::isNumber(invalid_json["age"])) {
std::cout << "✗ Age must be a number" << std::endl;
}
if (!JSONValidator::isStringLengthValid(invalid_json["email"], 5, 100)) {
std::cout << "✗ Email address length is invalid" << std::endl;
}
}
} catch (const json::exception& e) {
std::cerr << "JSON error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
Advanced Validation Features and Custom Verification
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <functional>
#include <map>
#include <optional>
using json = nlohmann::json;
// Validation result
struct ValidationResult {
bool is_valid;
std::vector<std::string> errors;
ValidationResult() : is_valid(true) {}
void addError(const std::string& error) {
errors.push_back(error);
is_valid = false;
}
void merge(const ValidationResult& other) {
if (!other.is_valid) {
is_valid = false;
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
}
}
};
// Advanced validator
class AdvancedJSONValidator {
private:
// Email address validation
static bool isValidEmail(const std::string& email) {
static const std::regex email_regex(
R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"
);
return std::regex_match(email, email_regex);
}
// URL validation
static bool isValidURL(const std::string& url) {
static const std::regex url_regex(
R"(^https?://[^\s/$.?#].[^\s]*$)"
);
return std::regex_match(url, url_regex);
}
// Date validation (ISO 8601 format)
static bool isValidDate(const std::string& date) {
static const std::regex date_regex(
R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$)"
);
return std::regex_match(date, date_regex);
}
public:
// Advanced string field validation
static ValidationResult validateStringField(const json& j, const std::string& field_name,
bool required = true,
size_t min_length = 0,
size_t max_length = std::numeric_limits<size_t>::max(),
const std::string& pattern = "",
const std::string& format = "") {
ValidationResult result;
// Check field existence
if (!j.contains(field_name)) {
if (required) {
result.addError("Required field '" + field_name + "' not found");
}
return result;
}
// Type check
if (!j[field_name].is_string()) {
result.addError("Field '" + field_name + "' must be a string");
return result;
}
std::string value = j[field_name].get<std::string>();
// Length check
if (value.length() < min_length || value.length() > max_length) {
result.addError("Field '" + field_name + "' length must be between " +
std::to_string(min_length) + " and " +
std::to_string(max_length) + " characters");
}
// Pattern check
if (!pattern.empty()) {
std::regex regex_pattern(pattern);
if (!std::regex_match(value, regex_pattern)) {
result.addError("Field '" + field_name + "' does not match pattern");
}
}
// Format check
if (!format.empty()) {
if (format == "email" && !isValidEmail(value)) {
result.addError("Field '" + field_name + "' is not a valid email address");
} else if (format == "url" && !isValidURL(value)) {
result.addError("Field '" + field_name + "' is not a valid URL");
} else if (format == "date" && !isValidDate(value)) {
result.addError("Field '" + field_name + "' is not a valid date format");
}
}
return result;
}
// Number field validation
static ValidationResult validateNumberField(const json& j, const std::string& field_name,
bool required = true,
double min_value = std::numeric_limits<double>::lowest(),
double max_value = std::numeric_limits<double>::max(),
bool integer_only = false) {
ValidationResult result;
// Check field existence
if (!j.contains(field_name)) {
if (required) {
result.addError("Required field '" + field_name + "' not found");
}
return result;
}
// Type check
if (!j[field_name].is_number()) {
result.addError("Field '" + field_name + "' must be a number");
return result;
}
double value = j[field_name].get<double>();
// Range check
if (value < min_value || value > max_value) {
result.addError("Field '" + field_name + "' value must be between " +
std::to_string(min_value) + " and " +
std::to_string(max_value));
}
// Integer check
if (integer_only && std::floor(value) != value) {
result.addError("Field '" + field_name + "' must be an integer");
}
return result;
}
// Array field validation
static ValidationResult validateArrayField(const json& j, const std::string& field_name,
bool required = true,
size_t min_items = 0,
size_t max_items = std::numeric_limits<size_t>::max(),
std::function<ValidationResult(const json&, size_t)> item_validator = nullptr) {
ValidationResult result;
// Check field existence
if (!j.contains(field_name)) {
if (required) {
result.addError("Required field '" + field_name + "' not found");
}
return result;
}
// Type check
if (!j[field_name].is_array()) {
result.addError("Field '" + field_name + "' must be an array");
return result;
}
const json& array = j[field_name];
// Size check
if (array.size() < min_items || array.size() > max_items) {
result.addError("Field '" + field_name + "' must have between " +
std::to_string(min_items) + " and " +
std::to_string(max_items) + " items");
}
// Validate each item
if (item_validator) {
for (size_t i = 0; i < array.size(); ++i) {
ValidationResult item_result = item_validator(array[i], i);
if (!item_result.is_valid) {
for (const auto& error : item_result.errors) {
result.addError("Array '" + field_name + "' element[" +
std::to_string(i) + "]: " + error);
}
}
}
}
return result;
}
// Object field validation
static ValidationResult validateObjectField(const json& j, const std::string& field_name,
bool required = true,
std::function<ValidationResult(const json&)> object_validator = nullptr) {
ValidationResult result;
// Check field existence
if (!j.contains(field_name)) {
if (required) {
result.addError("Required field '" + field_name + "' not found");
}
return result;
}
// Type check
if (!j[field_name].is_object()) {
result.addError("Field '" + field_name + "' must be an object");
return result;
}
// Object validation
if (object_validator) {
ValidationResult obj_result = object_validator(j[field_name]);
if (!obj_result.is_valid) {
for (const auto& error : obj_result.errors) {
result.addError("Object '" + field_name + "': " + error);
}
}
}
return result;
}
};
// Concrete schema validation example
class UserProfileValidator {
public:
static ValidationResult validateUserProfile(const json& user_profile) {
ValidationResult result;
// Basic information validation
auto name_result = AdvancedJSONValidator::validateStringField(
user_profile, "name", true, 2, 50
);
result.merge(name_result);
auto email_result = AdvancedJSONValidator::validateStringField(
user_profile, "email", true, 5, 100, "", "email"
);
result.merge(email_result);
auto age_result = AdvancedJSONValidator::validateNumberField(
user_profile, "age", true, 0, 120, true
);
result.merge(age_result);
// Skills validation
auto skills_result = AdvancedJSONValidator::validateArrayField(
user_profile, "skills", false, 0, 20,
[](const json& item, size_t index) -> ValidationResult {
ValidationResult item_result;
if (!item.is_string()) {
item_result.addError("Skill must be a string");
} else {
std::string skill = item.get<std::string>();
if (skill.empty() || skill.length() > 30) {
item_result.addError("Skill must be between 1 and 30 characters");
}
}
return item_result;
}
);
result.merge(skills_result);
// Contact information validation
auto contact_result = AdvancedJSONValidator::validateObjectField(
user_profile, "contact", false,
[](const json& contact) -> ValidationResult {
ValidationResult contact_result;
// Phone number validation
if (contact.contains("phone")) {
auto phone_result = AdvancedJSONValidator::validateStringField(
contact, "phone", false, 10, 15, R"(^\+?[1-9]\d{1,14}$)"
);
contact_result.merge(phone_result);
}
// Address validation
if (contact.contains("address")) {
auto address_result = AdvancedJSONValidator::validateStringField(
contact, "address", false, 10, 200
);
contact_result.merge(address_result);
}
// Website validation
if (contact.contains("website")) {
auto website_result = AdvancedJSONValidator::validateStringField(
contact, "website", false, 0, 200, "", "url"
);
contact_result.merge(website_result);
}
return contact_result;
}
);
result.merge(contact_result);
return result;
}
};
int main() {
try {
// Test valid user profile
{
json valid_profile = {
{"name", "John Doe"},
{"email", "[email protected]"},
{"age", 30},
{"skills", {"C++", "Python", "JavaScript", "SQL"}},
{"contact", {
{"phone", "+1234567890"},
{"address", "123 Main Street, City, State 12345"},
{"website", "https://example.com"}
}}
};
std::cout << "Valid user profile:\n" << valid_profile.dump(4) << std::endl;
ValidationResult result = UserProfileValidator::validateUserProfile(valid_profile);
if (result.is_valid) {
std::cout << "✓ User profile is valid" << std::endl;
} else {
std::cout << "✗ Validation errors:" << std::endl;
for (const auto& error : result.errors) {
std::cout << " - " << error << std::endl;
}
}
}
std::cout << "\n" << std::string(50, '-') << "\n\n";
// Test invalid user profile
{
json invalid_profile = {
{"name", "A"}, // Too short name
{"email", "invalid-email"}, // Invalid email
{"age", 150}, // Out of range age
{"skills", {"", "very long skill name that exceeds the maximum length limit"}}, // Invalid skills
{"contact", {
{"phone", "123"}, // Invalid phone number
{"address", "Short"}, // Too short address
{"website", "not-a-url"} // Invalid URL
}}
};
std::cout << "Invalid user profile:\n" << invalid_profile.dump(4) << std::endl;
ValidationResult result = UserProfileValidator::validateUserProfile(invalid_profile);
if (result.is_valid) {
std::cout << "✓ User profile is valid" << std::endl;
} else {
std::cout << "✗ Validation errors:" << std::endl;
for (const auto& error : result.errors) {
std::cout << " - " << error << std::endl;
}
}
}
} catch (const json::exception& e) {
std::cerr << "JSON error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
JSON Schema Integration and Schema-driven Validation
// Note: This code is an integration example with json-schema-validator library
// Additional libraries are required for actual use
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <vector>
using json = nlohmann::json;
// Schema definition class
class JSONSchemaManager {
private:
std::map<std::string, json> schemas;
public:
// Define and register schema
void defineSchema(const std::string& schema_name, const json& schema) {
schemas[schema_name] = schema;
}
// Load schema from file
bool loadSchemaFromFile(const std::string& schema_name, const std::string& file_path) {
try {
std::ifstream file(file_path);
if (!file.is_open()) {
return false;
}
json schema;
file >> schema;
schemas[schema_name] = schema;
return true;
} catch (const std::exception&) {
return false;
}
}
// Get schema
std::optional<json> getSchema(const std::string& schema_name) {
auto it = schemas.find(schema_name);
if (it != schemas.end()) {
return it->second;
}
return std::nullopt;
}
// Basic schema validation (simplified version)
ValidationResult validateAgainstSchema(const json& data, const std::string& schema_name) {
ValidationResult result;
auto schema_opt = getSchema(schema_name);
if (!schema_opt) {
result.addError("Schema '" + schema_name + "' not found");
return result;
}
json schema = *schema_opt;
// Type check
if (schema.contains("type")) {
std::string expected_type = schema["type"];
if (!checkType(data, expected_type)) {
result.addError("Type mismatch. Expected type: " + expected_type);
}
}
// Property check
if (schema.contains("properties") && data.is_object()) {
auto properties = schema["properties"];
for (auto it = properties.begin(); it != properties.end(); ++it) {
std::string prop_name = it.key();
json prop_schema = it.value();
if (data.contains(prop_name)) {
auto prop_result = validateProperty(data[prop_name], prop_schema, prop_name);
result.merge(prop_result);
}
}
}
// Required properties check
if (schema.contains("required") && data.is_object()) {
auto required = schema["required"];
for (const auto& req_prop : required) {
std::string prop_name = req_prop;
if (!data.contains(prop_name)) {
result.addError("Required property '" + prop_name + "' not found");
}
}
}
return result;
}
private:
bool checkType(const json& data, const std::string& expected_type) {
if (expected_type == "object") return data.is_object();
if (expected_type == "array") return data.is_array();
if (expected_type == "string") return data.is_string();
if (expected_type == "number") return data.is_number();
if (expected_type == "integer") return data.is_number_integer();
if (expected_type == "boolean") return data.is_boolean();
if (expected_type == "null") return data.is_null();
return false;
}
ValidationResult validateProperty(const json& value, const json& prop_schema, const std::string& prop_name) {
ValidationResult result;
// Type check
if (prop_schema.contains("type")) {
std::string expected_type = prop_schema["type"];
if (!checkType(value, expected_type)) {
result.addError("Property '" + prop_name + "' type mismatch");
}
}
// String length check
if (prop_schema.contains("minLength") && value.is_string()) {
int min_length = prop_schema["minLength"];
if (value.get<std::string>().length() < static_cast<size_t>(min_length)) {
result.addError("Property '" + prop_name + "' length below minimum");
}
}
if (prop_schema.contains("maxLength") && value.is_string()) {
int max_length = prop_schema["maxLength"];
if (value.get<std::string>().length() > static_cast<size_t>(max_length)) {
result.addError("Property '" + prop_name + "' length exceeds maximum");
}
}
// Number range check
if (prop_schema.contains("minimum") && value.is_number()) {
double minimum = prop_schema["minimum"];
if (value.get<double>() < minimum) {
result.addError("Property '" + prop_name + "' value below minimum");
}
}
if (prop_schema.contains("maximum") && value.is_number()) {
double maximum = prop_schema["maximum"];
if (value.get<double>() > maximum) {
result.addError("Property '" + prop_name + "' value exceeds maximum");
}
}
// Array length check
if (prop_schema.contains("minItems") && value.is_array()) {
int min_items = prop_schema["minItems"];
if (value.size() < static_cast<size_t>(min_items)) {
result.addError("Property '" + prop_name + "' array length below minimum");
}
}
if (prop_schema.contains("maxItems") && value.is_array()) {
int max_items = prop_schema["maxItems"];
if (value.size() > static_cast<size_t>(max_items)) {
result.addError("Property '" + prop_name + "' array length exceeds maximum");
}
}
return result;
}
};
// Usage example
int main() {
try {
JSONSchemaManager schema_manager;
// User profile schema definition
json user_schema = {
{"type", "object"},
{"properties", {
{"name", {
{"type", "string"},
{"minLength", 2},
{"maxLength", 50}
}},
{"email", {
{"type", "string"},
{"format", "email"}
}},
{"age", {
{"type", "integer"},
{"minimum", 0},
{"maximum", 120}
}},
{"skills", {
{"type", "array"},
{"items", {
{"type", "string"},
{"minLength", 1},
{"maxLength", 30}
}},
{"minItems", 0},
{"maxItems", 20}
}},
{"contact", {
{"type", "object"},
{"properties", {
{"phone", {
{"type", "string"},
{"pattern", "^\\+?[1-9]\\d{1,14}$"}
}},
{"address", {
{"type", "string"},
{"minLength", 10},
{"maxLength", 200}
}},
{"website", {
{"type", "string"},
{"format", "uri"}
}}
}}
}}
}},
{"required", {"name", "email", "age"}}
};
schema_manager.defineSchema("user_profile", user_schema);
// Valid data validation
{
json valid_data = {
{"name", "John Doe"},
{"email", "[email protected]"},
{"age", 30},
{"skills", {"C++", "Python", "JavaScript"}},
{"contact", {
{"phone", "+1234567890"},
{"address", "123 Main Street, City, State 12345"},
{"website", "https://example.com"}
}}
};
std::cout << "Valid data validation:\n";
ValidationResult result = schema_manager.validateAgainstSchema(valid_data, "user_profile");
if (result.is_valid) {
std::cout << "✓ Data is valid" << std::endl;
} else {
std::cout << "✗ Validation errors:" << std::endl;
for (const auto& error : result.errors) {
std::cout << " - " << error << std::endl;
}
}
}
std::cout << "\n" << std::string(50, '-') << "\n\n";
// Invalid data validation
{
json invalid_data = {
{"name", "A"}, // Too short
{"email", "invalid-email"}, // Invalid email
{"age", 150}, // Out of range
{"skills", {"", "very long skill name that exceeds maximum length"}}, // Invalid skills
{"contact", {
{"phone", "123"}, // Invalid phone
{"address", "Short"}, // Too short
{"website", "not-a-url"} // Invalid URL
}}
};
std::cout << "Invalid data validation:\n";
ValidationResult result = schema_manager.validateAgainstSchema(invalid_data, "user_profile");
if (result.is_valid) {
std::cout << "✓ Data is valid" << std::endl;
} else {
std::cout << "✗ Validation errors:" << std::endl;
for (const auto& error : result.errors) {
std::cout << " - " << error << std::endl;
}
}
}
// Save schema file
{
std::ofstream schema_file("user_profile_schema.json");
schema_file << user_schema.dump(4);
schema_file.close();
std::cout << "\nSchema file saved to user_profile_schema.json" << std::endl;
}
} catch (const json::exception& e) {
std::cerr << "JSON error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}