Search code examples
c++jsonrapidjson

I need help using RapidJSON


I'm trying to parse a JSON file using RapidJSON that has thousands of objects like this one

"Amateur Auteur": {
"layout": "normal",
"name": "Amateur Auteur",
"manaCost": "{1}{W}",
"cmc": 2,
"colors": [
  "White"
],
"type": "Creature — Human",
"types": [
  "Creature"
],
"subtypes": [
  "Human"
],
"text": "Sacrifice Amateur Auteur: Destroy target enchantment.",
"power": "2",
"toughness": "2",
"imageName": "amateur auteur",
"colorIdentity": [
  "W"
 ]
},

I believe I have stored the JSON as a C string correctly I just can't really understand how to use the RapidJSON library to return the values I want from each object.

This is the code for storing the JSON as a C string and then parsing it in case I am doing something incorrect here.

std::ifstream input_file_stream;
input_file_stream.open("AllCards.json", std::ios::binary | std::ios::ate); //Open file in binary mode and seek to end of stream 

if (input_file_stream.is_open())
{
    std::streampos file_size = input_file_stream.tellg(); //Get position of stream (We do this to get file size since we are at end)
    input_file_stream.seekg(0); //Seek back to beginning of stream to start reading
    char * bytes = new char[file_size]; //Allocate array to store data in
    if (bytes == nullptr)
    {
        std::cout << "Failed to allocate the char byte block of size: " << file_size << std::endl;
    }
    input_file_stream.read(bytes, file_size); //read the bytes
    document.Parse(bytes);
    input_file_stream.close(); //close file since we are done reading bytes
    delete[] bytes; //Clean up where we allocated bytes to prevent memory leak
}
else
{
    std::cout << "Unable to open file for reading.";
}

Solution

  • Your post seems to ask multiple questions. Lets start from the beginning.

    I believe I have stored the JSON as a C string correctly I just can't really understand how to use the RapidJSON library to return the values I want from each object.

    This is a big no no in software engineering. Never believe or assume. It will come back on release day and haunt you. Instead validate your assertion. Here are a few steps starting from the easy to the more involved.

    1. Print out your C-String.

    The easiest way to confirm the content of your variable, especially string data, is to simply print to the screen. Nothing easier then seeing your JSON data print to the screen to confirm you have read it in correctly.

    std::cout.write(bytes, filesize);
    
    1. Breakpoint / Debugger

    If you have some reason for not printing out your variable, then compile your code with debugging enabled and load in GDB if you're using g++, lldb if you're using clang++, or simply place a breakpoint in visual studio if you're using an VS or VSCode. Once at the breakpoint you can inspect the content of your variable.

    However, before we move on I wouldn't be helping you if I didn't point out that reading files in CPP is much much easier then the way you're reading.

        // Open File
        std::ifstream in("AllCards.json", std::ios::binary);
        if(!in)
            throw std::runtime_error("Failed to open file.");
    
        // dont skip on whitespace
        std::noskipws(in);
    
        // Read in content
        std::istreambuf_iterator<char> head(in);
        std::istreambuf_iterator<char> tail;
        std::string data(head, tail);
    

    At the end of the above code you now have all of the content from your file read into an std::string which wraps a null terminated or C-String. You can access that data by calling .c_str() on your string instances. If you do it this way you no longer have to worry about calling new or delete[] as the std::string class takes care of the buffer for you. Just make sure it hangs around as long as you're using it in RapidJSON.

    This is the code for storing the JSON as a C string and then parsing it in case I am doing something incorrect here.

    No. according the the rapid JSON documentation you create a document object and have it parse the string.

    Document d;
    d.Parse(data.c_str());
    

    However, that just creates the element for querying the document. You can ask the document if specific items exist (d.hasMember("")), ask for a string typed members content d["name"].GetString() or anything listed over at the documentation. You can read the tutorial here.

    What exactly are you trying to do with the parsed JSON element?

    I just can't really understand how to use the RapidJSON library to return the values I want from each object.

    I cannot answer this question for two reasons. What are you trying to extract? What have you tried? Have you read the documentation and do not understand a specific item?

    ** Updates ** To your question. You can iterate over all objects.

     using namespace rapidjson;
         Document d;
         d.Parse(data.c_str());
         for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr){
                 std::cout << itr->name.GetString() << '\n';
         }