Candag Candag - 4 months ago 58
JSON Question

Rapidjson returning reference to Document Value

I'm having some trouble with the following method and I need some help trying to figure out what I am doing wrong.

I want to return a reference to a Value in a document. I am passing the Document from outside the function so that when I read a json file into it I don't "lose it".

const rapidjson::Value& CTestManager::GetOperations(rapidjson::Document& document)
{
const Value Null(kObjectType);

if (m_Tests.empty())
return Null;

if (m_current > m_Tests.size() - 1)
return Null;

Test& the_test = m_Tests[m_current];

CMyFile fp(the_test.file.c_str()); // non-Windows use "r"
if (!fp.is_open())
return Null;

u32 operations_count = 0;

CFileBuffer json(fp);
FileReadStream is(fp.native_handle(), json, json.size());

if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("td_tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];

if (test["id"].GetInt() == the_test.id)
{
auto it = test.FindMember("operations");
if (it != test.MemberEnd())
{
//return it->value; is this legitimate?
return test["operations"];
}

return Null;
}
}
}
}
}

return Null;
}


Which I am calling like this:

Document document;
auto operations = TestManager().GetOperations(document);


When I inspect the value of
test["operations"]
inside the function I can see everything I would expect (debug code removed from the abode code).

When I inspect the returned value outside the function I can see that it's an array (which I expect). the member count int the array is correct as well, but when print it out, I only see garbage instead.

When I "print" the Value to a string inside the methods, I get what I expect (i.e. a well formated json), but when I do it outside all keys show up as "IIIIIIII" and values that aren't strings show up correctly.

rapidjson::StringBuffer strbuf2;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer2(strbuf2);
ops->Accept(writer2);


As this didn't work I decided to change the method to receive a Value as a parameter and do a deep copy into it like this

u32 CTestManager::GetOperationsEx(rapidjson::Document& document, rapidjson::Value& operations)
{
(...)

if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];

if (test["id"].GetInt() == the_test.id)
{
const Value& opv = test["operations"];

Document::AllocatorType& allocator = document.GetAllocator();
operations.CopyFrom(opv, allocator); //would Swap work?
return operations.Size();
}
}
}
}
}

return 0;
}


Which I'm calling like this:

Document document;
Value operations(kObjectType);
u32 count = TestManager().GetOperationsEx(document, operations);


But... I get same thing!!!!

I know that it's going to be something silly but I can't put my hands on it!

Any ideas?

Answer

The problem in this case lies with the use of ParseInSitu. When any of the GetOperations exist the CFileBuffer loses scope and is cleaned up. Because the json is being parsed in-situ when the buffer to the file goes, so goes the data.

Comments