4

I am facing a strange problem : when i try to add a Json variable inside a for loop, it is not written properly in the output file whereas it works well outside the loop (rapidJson v0.11).

Edit : the loop is not the problem but the bug appears even only with brackets

Here is a sample of my code :

rapidjson::Document output;
output.SetObject();
rapidjson::Document::AllocatorType& allocator = output.GetAllocator();

{
    std::string s1("test");
    output.AddMember("test_field",s1.c_str(), allocator);
}
std::string s2("test");
output.AddMember("test_field2",s2.c_str(), allocator);

rapidjson::FileStream f(stdout);
rapidjson::PrettyWriter<rapidjson::FileStream> writer(f);
output.Accept(writer);

The output I get is :

{"test_field": "\u0000est", 
"test_field2": "test"}

So there seems to be a problem with the variable added inside the bracket.. Do you have any idea where it comes from ?

Arcyno
  • 4,153
  • 3
  • 34
  • 52
  • I wonder if it's because `s1` exists on the stack and is being destroyed at the closing brace of your block, and rapidjson is expecting it to keep existing. Just a guess. – Steve Apr 15 '15 at 13:25
  • It has to be something like that... but the strangest part is that i just lose the first letter ("t") and the other ones are not destroyed ("est")... – Arcyno Apr 15 '15 at 13:32
  • Try simply moving `s1` out of the block, but still assign `"test"` to it and call `AddMember()` within the block. If this change improves the behavior, I'd be willing to bet this is the problem. – Steve Apr 15 '15 at 14:21
  • 1
    Like I said, I don't know a thing about rapidjson, but you might want to read the section titled [`Manipulating String` in the rapidjson documentation](https://code.google.com/p/rapidjson/wiki/UserGuide#Manipulating_String). – Steve Apr 15 '15 at 14:26

2 Answers2

4

The following call:

output.AddMember("test_field",s1.c_str(), allocator);

treats the second parameter as a constant string. This is not OK because s1 will be destroyed out of the block.

You need to make a copy of the string. A solution is:

Value v(s1.c_str(), allocator);
output.AddMember("test_field", v, allocator);

Or equivalently:

output.AddMember("test_field", Value(s1.c_str(), allocator).Move(), allocator);

And a faster (also better) version is:

output.AddMember("test_field", Value(s1.c_str(), s1.size(), allocator).Move(), allocator);

Because this does not need to call a strlen()-like function to find the length of s1. And it can handle null character inside a string.

Milo Yip
  • 4,902
  • 2
  • 25
  • 27
2

Thanks to Steve I managed to find the answer :

as said in the documentation : "rapidjson provide two strategies for storing string.

  1. copy-string: allocates a buffer, and then copy the source data into it.
  2. const-string: simply store a pointer of string. "

So the right way to do what I wanted is :

rapidjson::Document output;
output.SetObject();
rapidjson::Document::AllocatorType& allocator = output.GetAllocator();

{
    std::string s1("test");
    rapidjson::Value field;
    char buffer[10];
    int len = sprintf(buffer, s1); .
    field.SetString(buffer, len, allocator);

    output.AddMember("test_field",field, allocator);
}
std::string s2("test");
output.AddMember("test_field2",s2.c_str(), allocator);

rapidjson::FileStream f(stdout);
rapidjson::PrettyWriter<rapidjson::FileStream> writer(f);
output.Accept(writer);

The output I get is now :

 {"test_field": "test", 
  "test_field2": "test"}
Arcyno
  • 4,153
  • 3
  • 34
  • 52