2

How do I convert a rapidjson array iterator to a rapidjson::value?

I do not want answers that focus on how to get the contents of a rapid json array, or how to iterate through it.

I am also very aware that I can access members through the iterator using the example in the rapidjson documentation itr->name, but that is not what I want either. That form of working with rapidjson arrays already appears on many stack overflow questions and the rapidjson docs, and has been covered.

I need to end up with a rapidjson::value when starting with a rapidjson array iterator.

If we have an std::vector<int> than we can assign

std::vector<int>::iterator itMyInt = myvector.begin();
const int & myInt = *itMyInt;

I would expect to be able to do the same thing with a rapidjson array iterator, but my compiler disagrees.

The reason I need a rapidjson::value is that I'd like to reuse the same parsing method to parse the json object when is an element of an array, as I would when parsing that object on its own and not in an array.

Let me demonstrate with my minimal example:

// Rapid JSON Includes
#include <rapidjson/Document.h>
#include <rapidjson/StringBuffer.h>
#include <rapidjson/writer.h>

// Standard Includes
#include <exception>
#include <string>
#include <sstream>

//--------------------------------------------------------------------------------------------------
// NOTE -This stub cannot change
void ParseCar(const rapidjson::Value & carJson)
{

}

//--------------------------------------------------------------------------------------------------
int main()
{
    std::string json =
        "{"
        "    \"cars\" : ["
        "    {"
        "        \"name\" : \"Fiat\","
        "        \"price\" : 19.95"
        "    },"
        "    {"
        "        \"name\" : \"FRS\","
        "        \"price\" : 19995.00"
        "    }]"
        "}";

    // Parse the JSON
    rapidjson::Document document;
    document.Parse(json.c_str());

    if (document.HasParseError())
    {
        // Error - Failed to parse JSON
        std::ostringstream msg;
        msg << "There was an error parsing the JSON"
            << " Error Code: " << document.GetParseError()
            << " Error Offset: " << document.GetErrorOffset();

        throw std::exception(msg.str().c_str());
    }

    // Cars array
    if (!document.HasMember("cars") ||
        !document["cars"].IsArray())
    {
        std::string msg("Expected \"cars\"  JSON array");

        throw std::exception(msg.c_str());
    }

    const rapidjson::Value & carsArrayJSON = document["cars"];

    /* Doesn't compile - No GetArray method exists
    for (auto & carJSON : carsArrayJSON.GetArray())
    {

    }
    */


    for (rapidjson::Value::ConstMemberIterator itCarJSON = carsArrayJSON.MemberBegin(); itCarJSON != carsArrayJSON.MemberEnd(); ++itCarJSON)
    {
        // Error - const rapidjson::GenericMember<Encoding,Allocator>' to 'const rapidjson::Value
        const rapidjson::Value & carJSON = *itCarJSON;

        ParseCar(carJSON);
    }

    return 0;
}
Christopher Pisz
  • 3,757
  • 4
  • 29
  • 65
  • Note that you can also use raw string literals to avoid having to escape everything manually http://en.cppreference.com/w/cpp/language/string_literal ((6) in that link) – Curious Jun 07 '17 at 18:49

2 Answers2

1

MemberIterator iterates Members of an Object. ValueIterator iterates Values in an Array.

Also, I'm not sure why that loop had issues for you, this compiles for me:

std::string json =
    "{"
    "    \"cars\" : ["
    "    {"
    "        \"name\" : \"Fiat\","
    "        \"price\" : 19.95"
    "    },"
    "    {"
    "        \"name\" : \"FRS\","
    "        \"price\" : 19995.00"
    "    }]"
    "}";
Document doc;
doc.Parse(json.data());
Value const& cars = doc["cars"];
for (auto& car : cars.GetArray()) {
    cout << "name: " << car["name"].GetString() << " "
        << "price: " << car["price"].GetFloat() << endl;
}

And returns:

name: Fiat price: 19.95
name: FRS price: 19995

As for applying your function in a loop:

for (Value& car : cars.GetArray()) {
    ParseCar(car);
}

If you must use an iterator:

for (Value::ValueIterator car = cars.Begin(); car != cars.End(); ++car) {
    ParseCar(*car);
}

Or with auto:

for (auto car = cars.Begin(); car != cars.End(); ++car) {
    ParseCar(*car);
}
Josh Olson
  • 397
  • 2
  • 15
0

Note: this will only work in an array.

Recently, I faced a similar problem and I came out with a different approach.

First, I set up a pointer to the desired path like so:

int myIterator = 0;
string myString = "/Layer1/Layer2/" + to_string(myIterator);
Pointer p(myString.c_str());

I now have a pointer to a variable path in my JSON object that varies with myIterator.

Then, I just used a for loop in which I use the SetValueByPointer function to append the pointer's value to a JSON object.

Community
  • 1
  • 1
Jacobb
  • 41
  • 5