Post

C++ JSON

Logo

This guide will provide step-by-step instructions on how to add JSON to the Visual Studio C++ project.

In computing, serialization is the process of translating a data structure or object state into a format that can be stored or transmitted and reconstructed later. The reverse process is called deserialization. The game engine will use JSON to deserialize file(s) that define game objects in the game level.

About JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

Download JSON

Download
  • Copy the .zip into the ThirdParty folder
  • Extract the rapidjson-master.zip file
  • Rename the extracted folder “rapidjson”
Zip
  • Delete the rapidjson-master.zip file, it is not needed

Add JSON to the Solution Project(s)

If the Solution contains multiple Projects, the following steps will need to be done for each project. This is because each project needs the path to the JSON includes.

Projects

In the Project Properties, make sure that the Configuration is set to All Configurations and Platform is set to All Platforms.

Configurations
  • Add the directory of the FMOD include folder to the Additional Include Directories.
    • Additional Include Directories is located in C/C++>General.
    • Add $(SolutionDir)ThirdParty\rapidjson\include
1
$(SolutionDir)ThirdParty\rapidjson\include
Include

Create JSON File

  • In the Build/Assets folder create a text file called json.txt
File
  • Open the file and add JSON data
    1
    2
    3
    4
    5
    6
    7
    8
    
    {
      "name": "Raymond",
      "age": 44,
      "speed": 18.5,
      "isAwake": true,
      "position": [10, 20],
      "color": [1, 0, 0, 1]
    }
    
  • The data is in a key, value format
    • “key”: value
  • Using the key, the value can be retrieved

It is common to have the JSON file not correctly formatted. Use this page to verify your JSON file: https://jsonformatter.curiousconcept.com/

  • Copy the contents of the JSON file and paste it into the page
  • Click Process
  • It will notify you of any errors if they exist
Formatter

Create JSON Functions

  • Create a Json.h and Json.cpp in the Core filter, make sure the file is in the source/core folder
Json

Create JSON header (.h)

  • In the Json.h file, add code for the function declarations to load and read the JSON file
    • The functions are placed in a namespace to keep the function names in their own space
1
2
3
4
5
6
7
8
#include <string>
#include<rapidjson/document.h>

namespace Json
{
	bool Load(const std::string& filename, rapidjson::Document& document);
	bool Read(const rapidjson::Value& value, const std::string& name, int& data);
}

Create JSON source file (.cpp)

  • In the Json.cpp file, add code for the function definitions to load and read the JSON file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "Json.h"
#include "EFile.h"

#include <rapidjson/istreamwrapper.h>
#include <iostream>

namespace Json
{
    bool Load(const std::string& filename, rapidjson::Document& document)
    {
        // read the file into a string
        std::string buffer;
        if (!File::ReadFile(filename, buffer))
        {
            return false;
        }

        // convert the string into a json stream
        std::stringstream stream(buffer);
        rapidjson::IStreamWrapper istream(stream);

        // set the json document from the stream
        document.ParseStream(istream);
        // check if the parse was successful
        if (!document.IsObject())
        {
            std::cerr << "Could not parse Json: " << filename << std::endl;
            return false;
        }

        return true;
    }

    bool Read(const rapidjson::Value& value, const std::string& name, int& data)
    {
        // check if the value has the "<name>" and the correct data type
        if (!value.HasMember(name.c_str()) || !value[name.c_str()].IsInt())
        {
            std::cerr << "Could not read Json value: " << name << std::endl;
            return false;
        }

        // get the data
        data = value[name.c_str()].GetInt();

        return true;
    }
}
  • Include the **Json.h## in the Engine.h
    • Keep the include with the other Core includes
1
#include "Core/Json.h"

Load and Read JSON in Main()

  • In the Main.cpp main() function, add the code to read the JSON data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// !! this code is not neccessary, it just shows the contents of the file !!
std::string buffer;
File::ReadFile("json.txt", buffer);
// show the contents of the json file
std::cout << buffer << std::endl;

// create json document from the json file contents
rapidjson::Document document;
Json::Load("json.txt", document);

// read the name data from the json
std::string name;
Json::Read(document, "name", name);
// show the name data
std::cout << name << std::endl;
  • After running the program, the console will display the contents of the JSON file and the name data
Output

Add Addition JSON Functions

Add additional functions to load different data types from the JSON

  • In the Json.h file, add the following functions
    • Include Vector2.h and Color.h
    • Add new functions to load float, bool, std::string, Vector2, and Color ``` #include “Math/Vector2.h” #include “Math/Color.h”

bool Read(const rapidjson::Value& value, const std::string& name, float& data); bool Read(const rapidjson::Value& value, const std::string& name, bool& data); bool Read(const rapidjson::Value& value, const std::string& name, std::string& data); bool Read(const rapidjson::Value& value, const std::string& name, Vector2& data); bool Read(const rapidjson::Value& value, const std::string& name, Color& data);

1
2
3
4
5
6
+ Create the definitions for the functions in Json.cpp
+ The code to get the data for each data type is similar to the int Read()
  + Change the functions for the data type in Is**DataType**() and Get**DataType**() function, here is an example for the bool data
  + Do this for the **bool**, **float**, and **std::string** Read() functions

bool Read(const rapidjson::Value& value, const std::string& name, bool& data) { // check if the value has the “" and the correct data type if (!value.HasMember(name.c_str()) || !value[name.c_str()].IsBool()) { std::cerr << "Could not read Json value: " << name << std::endl; return false; }

1
2
3
4
// get the data
data = value[name.c_str()].GetBool();

return true; } ```
  • The Vector2 and Color have multiple values that need to be read
    • JSON treats these as arrays and need to be read in as an array
  • Here is an example of reading in the Vector2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool Read(const rapidjson::Value& value, const std::string& name, Vector2& data)
{
    // check if the value has the "<name>" and is an array with 2 elements
    if (!value.HasMember(name.c_str()) || !value[name.c_str()].IsArray() || value[name.c_str()].Size() != 2)
    {
        std::cerr << "Could not read Json value: " << name << std::endl;
        return false;
    }

    // get json array object
    auto& array = value[name.c_str()];
    // get array values
    for (rapidjson::SizeType i = 0; i < array.Size(); i++)
    {
        if (!array[i].IsNumber())
        {
            std::cerr << "Could not read Json value: " << name << std::endl;
            return false;
        }

        // get the data
        data[i] = array[i].GetFloat();
    }

    return true;
}
  • Using the Vector2 as an example, complete the Color Read()
    • The color has 4 elements
      • Make sure when checking the array size, you check for 4 elements

Read Data Types in Main()

  • Update the code in main() to read and display all the data types
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// read the data from the json
std::string name;
int age;
float speed;
bool isAwake;
Vector2 position;
Color color;

Json::Read(document, "name", name);
Json::Read(document, "age", age);
Json::Read(document, "speed", speed);
Json::Read(document, "isAwake", isAwake);
Json::Read(document, "position", position);
Json::Read(document, "color", color);
// show the data
std::cout << name << " " << age << " " << speed << " " << isAwake << std::endl;
std::cout << position.x << " " << position.y << std::endl;
std::cout << color.r << " " << color.g << " " << color.b << " " << color.a << std::endl;
  • The output should look like the image below
Output All

Create JSON Read Macro

To make reading data in easier, a macro can be created to simplify the code. A macro in C++ is a preprocessor directive that defines a code fragment or value to be substituted and expanded before the actual compilation process begins.

  • In the Json.h file, add the following macro
    • The # in a macro converts the data parameter to a string by putting quotes around the name
1
#define READ_DATA(value, data) Json::Read(value, #data, data)
  • In main() change the Read() function to the macro
    • For the macro to work, the key in the JSON file must match the variable name in the code

Before

1
Json::Read(document, "name", name);

After

1
READ_DATA(document, name);
  • In main() change all the read functions to use the macro
1
2
3
4
5
6
READ_DATA(document, name);
READ_DATA(document, age);
READ_DATA(document, speed);
READ_DATA(document, isAwake);
READ_DATA(document, position);
READ_DATA(document, color);
This post is licensed under CC BY 4.0 by the author.