DEV Community

Amirmohammad Kazemi
Amirmohammad Kazemi

Posted on

JSON files in C++ (RapidJSON)

A JSON file (short for JavaScript Object Notation) is a lightweight, human-readable text format used for data exchange. It is commonly used to transfer data between a server and a client and is highly popular due to its simplicity and readability. JSON uses a key-value pair structure and supports data types such as strings, numbers, arrays, objects, and boolean values.

General Structure of JSON:

  • Data is written as key-value pairs.
  • Keys must be enclosed in double quotes (").
  • Values can be strings, numbers, arrays, other objects, true, false, or null.
  • Key-value pairs are separated by commas (,).
  • Objects are enclosed in curly braces ({}).
  • Arrays are enclosed in square brackets ([]).

Simple Example of a JSON File:

{
  "name": "John Doe",
  "age": 30,
  "isStudent": false,
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "state": "CA"
  },
  "phoneNumbers": [
    {"type": "home", "number": "123-456-7890"},
    {"type": "work", "number": "987-654-3210"}
  ],
  "email": "john.doe@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Explanation of the Example:

  1. name: A key with a string value ("John Doe").
  2. age: A key with a numeric value (30).
  3. isStudent: A key with a boolean value (false).
  4. address: A key with an object value containing address information.
  5. phoneNumbers: A key with an array value containing two objects. Each object includes the type of phone number (type) and the phone number itself (number).
  6. email: A key with a string value ("john.doe@example.com").

Uses of JSON:

  • Data Storage: For example, in configuration files or temporary data storage.
  • Data Exchange: For instance, in APIs, data is often exchanged between servers and clients in JSON format.
  • Software Configuration: Many software applications use JSON to store settings.

Another Example (An Array of Objects):

[
  {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com"
  },
  {
    "id": 2,
    "name": "Bob",
    "email": "bob@example.com"
  }
]
Enter fullscreen mode Exit fullscreen mode

In this example, there is an array of two objects, where each object contains information about a user.

JSON is a highly flexible and simple format that can easily be used in most programming languages such as JavaScript, Python, Java, C#, and more.

Get start with RapidJSON

To work with JSON files in C++, there are several libraries available, and one of the most popular is RapidJSON. RapidJSON is a fast and lightweight library for processing JSON in C++. Below is a comprehensive explanation of how to use RapidJSON to work with JSON files.

Steps to Use RapidJSON in C++

1. Installing RapidJSON

First, you need to add RapidJSON to your project. There are two main ways to do this:

  • Direct Download: Download the library files from the official RapidJSON GitHub page and add them to your project.
  • Using a Package Manager (e.g., vcpkg): If you are using vcpkg, you can install RapidJSON with the following command:
  vcpkg install rapidjson
Enter fullscreen mode Exit fullscreen mode

2. Adding RapidJSON to Your Project

After downloading or installing RapidJSON, you need to include its header files in your project.

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
#include <fstream>
Enter fullscreen mode Exit fullscreen mode

3. Reading and Parsing a JSON File

To read and parse a JSON file, follow these steps:

Example: Reading a JSON File

Assume you have a JSON file named data.json with the following content:

{
  "name": "John Doe",
  "age": 30,
  "isStudent": false,
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "state": "CA"
  },
  "phoneNumbers": [
    {"type": "home", "number": "123-456-7890"},
    {"type": "work", "number": "987-654-3210"}
  ]
}
Enter fullscreen mode Exit fullscreen mode

C++ code to read and parse this file:

#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"
#include <fstream>
#include <iostream>

int main() {
    // Open the JSON file
    std::ifstream ifs("data.json");
    if (!ifs.is_open()) {
        std::cerr << "Failed to open file!" << std::endl;
        return 1;
    }

    // Read and parse the file
    rapidjson::IStreamWrapper isw(ifs);
    rapidjson::Document doc;
    doc.ParseStream(isw);

    // Check for parsing errors
    if (doc.HasParseError()) {
        std::cerr << "Parse error: " << doc.GetParseError() << std::endl;
        return 1;
    }

    // Access JSON values
    const std::string name = doc["name"].GetString();
    int age = doc["age"].GetInt();
    bool isStudent = doc["isStudent"].GetBool();

    const std::string street = doc["address"]["street"].GetString();
    const std::string city = doc["address"]["city"].GetString();
    const std::string state = doc["address"]["state"].GetString();

    // Print values
    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Is Student: " << (isStudent ? "Yes" : "No") << std::endl;
    std::cout << "Address: " << street << ", " << city << ", " << state << std::endl;

    // Access arrays
    const rapidjson::Value& phoneNumbers = doc["phoneNumbers"];
    for (rapidjson::SizeType i = 0; i < phoneNumbers.Size(); i++) {
        const std::string type = phoneNumbers[i]["type"].GetString();
        const std::string number = phoneNumbers[i]["number"].GetString();
        std::cout << "Phone Type: " << type << ", Number: " << number << std::endl;
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

4. Creating and Writing a JSON File

To create a new JSON file and write data to it, follow these steps:

Example: Creating a JSON File
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <fstream>
#include <iostream>

int main() {
    // Create a Document object
    rapidjson::Document doc;
    doc.SetObject();

    // Add values to the Document
    rapidjson::Value name("John Doe");
    doc.AddMember("name", name, doc.GetAllocator());

    doc.AddMember("age", 30, doc.GetAllocator());
    doc.AddMember("isStudent", false, doc.GetAllocator());

    // Create an object for the address
    rapidjson::Value address(rapidjson::kObjectType);
    address.AddMember("street", "123 Main St", doc.GetAllocator());
    address.AddMember("city", "Anytown", doc.GetAllocator());
    address.AddMember("state", "CA", doc.GetAllocator());
    doc.AddMember("address", address, doc.GetAllocator());

    // Create an array for phone numbers
    rapidjson::Value phoneNumbers(rapidjson::kArrayType);

    rapidjson::Value phone1(rapidjson::kObjectType);
    phone1.AddMember("type", "home", doc.GetAllocator());
    phone1.AddMember("number", "123-456-7890", doc.GetAllocator());
    phoneNumbers.PushBack(phone1, doc.GetAllocator());

    rapidjson::Value phone2(rapidjson::kObjectType);
    phone2.AddMember("type", "work", doc.GetAllocator());
    phone2.AddMember("number", "987-654-3210", doc.GetAllocator());
    phoneNumbers.PushBack(phone2, doc.GetAllocator());

    doc.AddMember("phoneNumbers", phoneNumbers, doc.GetAllocator());

    // Convert Document to a JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    // Write the JSON string to a file
    std::ofstream ofs("output.json");
    if (!ofs.is_open()) {
        std::cerr << "Failed to open file for writing!" << std::endl;
        return 1;
    }
    ofs << buffer.GetString();
    ofs.close();

    std::cout << "JSON file created successfully!" << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

5. Compiling and Running

To compile the program, you need to link RapidJSON to your project. If you are using g++, run the following command:

g++ -o json_example json_example.cpp
Enter fullscreen mode Exit fullscreen mode

Then, run the program:

./json_example
Enter fullscreen mode Exit fullscreen mode

Important Notes:

  • Memory Management: RapidJSON uses an allocator for memory management. When adding values to a Document, always use the allocator.
  • Error Checking: Always check for parsing errors to avoid program crashes.
  • Code Readability: For complex JSON structures, break your code into smaller functions to improve readability.

Using RapidJSON, you can easily work with JSON files in C++. It is a fast and efficient library, suitable for both small and large projects.

Advance use

Yes, RapidJSON has many more methods and features that allow you to work with JSON in more advanced ways. Below, we explain some of the key methods and features of RapidJSON with examples.

1. Modifying Existing Values in JSON

You can modify existing values in a JSON object.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    const char* json = R"({"name":"John","age":30})";
    rapidjson::Document doc;
    doc.Parse(json);

    // Modify the value of "age"
    doc["age"] = 31;

    // Convert Document to JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "Modified JSON: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

2. Removing a Member from JSON

You can remove a member (key-value pair) from a JSON object.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    const char* json = R"({"name":"John","age":30})";
    rapidjson::Document doc;
    doc.Parse(json);

    // Remove the "age" member
    doc.RemoveMember("age");

    // Convert Document to JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "JSON after removal: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

3. Checking for a Specific Key

You can check whether a specific key exists in the JSON.

Example:

#include "rapidjson/document.h"
#include <iostream>

int main() {
    const char* json = R"({"name":"John","age":30})";
    rapidjson::Document doc;
    doc.Parse(json);

    // Check if a key exists
    if (doc.HasMember("name")) {
        std::cout << "Key 'name' exists!" << std::endl;
    } else {
        std::cout << "Key 'name' does not exist!" << std::endl;
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

4. Working with Arrays

You can create, modify, or iterate over arrays in JSON.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    const char* json = R"({"fruits":["apple","banana","cherry"]})";
    rapidjson::Document doc;
    doc.Parse(json);

    // Access the array
    rapidjson::Value& fruits = doc["fruits"];
    for (rapidjson::SizeType i = 0; i < fruits.Size(); i++) {
        std::cout << "Fruit " << i << ": " << fruits[i].GetString() << std::endl;
    }

    // Add a new member to the array
    rapidjson::Value newFruit("orange", doc.GetAllocator());
    fruits.PushBack(newFruit, doc.GetAllocator());

    // Convert Document to JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "Updated JSON: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

5. Creating JSON from Scratch

You can create a JSON object from scratch.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    rapidjson::Document doc;
    doc.SetObject();

    // Add values
    rapidjson::Value name("Alice");
    doc.AddMember("name", name, doc.GetAllocator());

    doc.AddMember("age", 25, doc.GetAllocator());

    // Create an array
    rapidjson::Value hobbies(rapidjson::kArrayType);
    hobbies.PushBack("reading", doc.GetAllocator());
    hobbies.PushBack("swimming", doc.GetAllocator());
    doc.AddMember("hobbies", hobbies, doc.GetAllocator());

    // Convert Document to JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "Created JSON: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

6. Converting JSON to String and Vice Versa

You can convert a JSON object to a string and vice versa.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    // Convert JSON to string
    rapidjson::Document doc;
    doc.SetObject();
    doc.AddMember("name", "Bob", doc.GetAllocator());

    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::string jsonString = buffer.GetString();
    std::cout << "JSON String: " << jsonString << std::endl;

    // Convert string to JSON
    rapidjson::Document newDoc;
    newDoc.Parse(jsonString.c_str());

    if (newDoc.HasParseError()) {
        std::cerr << "Parse error!" << std::endl;
        return 1;
    }

    std::cout << "Name from parsed JSON: " << newDoc["name"].GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

7. Working with Different Data Types

RapidJSON supports various data types such as numbers, strings, booleans, and null.

Example:

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    rapidjson::Document doc;
    doc.SetObject();

    // Add different data types
    doc.AddMember("string", "Hello, World!", doc.GetAllocator());
    doc.AddMember("number", 42, doc.GetAllocator());
    doc.AddMember("boolean", true, doc.GetAllocator());
    doc.AddMember("null", rapidjson::Value(), doc.GetAllocator());

    // Convert Document to JSON string
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "JSON with different data types: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

8. Using Pointer for Accessing Values

RapidJSON supports Pointer (e.g., JSON Pointer) for accessing values within JSON.

Example:

#include "rapidjson/document.h"
#include "rapidjson/pointer.h"
#include <iostream>

int main() {
    const char* json = R"({"name":"John","address":{"city":"New York"}})";
    rapidjson::Document doc;
    doc.Parse(json);

    // Use Pointer to access a value
    rapidjson::Value* city = rapidjson::Pointer("/address/city").Get(doc);
    if (city) {
        std::cout << "City: " << city->GetString() << std::endl;
    } else {
        std::cerr << "City not found!" << std::endl;
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

9. Using Schema for JSON Validation

RapidJSON supports Schema for validating the structure of JSON.

Example:

#include "rapidjson/document.h"
#include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    const char* json = R"({"name":"John","age":30})";
    const char* schemaJson = R"({"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}}})";

    rapidjson::Document doc;
    doc.Parse(json);

    rapidjson::Document schemaDoc;
    schemaDoc.Parse(schemaJson);

    rapidjson::SchemaDocument schema(schemaDoc);
    rapidjson::SchemaValidator validator(schema);

    if (!doc.Accept(validator)) {
        rapidjson::StringBuffer sb;
        validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
        std::cerr << "Invalid schema: " << sb.GetString() << std::endl;
    } else {
        std::cout << "JSON is valid!" << std::endl;
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

10. Using PrettyWriter for Pretty Output

You can use PrettyWriter to generate nicely formatted JSON output.

Example:

#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>

int main() {
    rapidjson::Document doc;
    doc.SetObject();
    doc.AddMember("name", "Alice", doc.GetAllocator());
    doc.AddMember("age", 25, doc.GetAllocator());

    // Use PrettyWriter
    rapidjson::StringBuffer buffer;
    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
    doc.Accept(writer);

    std::cout << "Pretty JSON: " << buffer.GetString() << std::endl;
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

RapidJSON is a powerful and flexible library for working with JSON in C++. Using its methods and features, you can easily create, modify, parse, and validate JSON. It is suitable for both small and large projects and offers excellent performance.

Top comments (0)