Writing in order to jsoncpp (c++)

9k views Asked by At

Consider the following example for which my source is

    Json::Value root;
    root["id"]=0;
            Json::Value text;
            text["first"]="i";
            text["second"]="love";
            text["third"]="you";
    root["text"]=text;
    root["type"]="test";
    root["begin"]=1;
    root["end"]=1;
    Json::StyledWriter writer;
    string strJson=writer.write(root);
    cout<<"JSON WriteTest" << endl << strJson <<endl;

I thought I'd write the json fields in the order of the lines. Instead the result is:

JSON WriteTest
{
   "begin" : 1,
   "end" : 1,
   "id" : 0,
   "text" : {
      "first" : "i",
      "second" : "love",
      "third" : "you"
   },
   "type" : "test"
}

I want json format is

JSON WriteTest
{
   "id" : 0,
   "text" : {
      "first" : "i",
      "second" : "love",
      "third" : "you"
   },
   "type" : "test"
   "begin" : 1,
   "end" : 1,
}

How can I write a Json order?

5

There are 5 answers

4
cwfighter On

I have a way can solve your problem. Would you like to try? My solution is that you use boost/property_tree/json_parser.hpp, the output is what format you want! About There is my code:

#include <boost/property_tree/json_parser.hpp>
#include <sstream>
#include <iostream>

using namespace std;
int main()
{
    boost::property_tree::ptree parser, child;
    parser.put("id", 0);
    child.put("first", "i");
    child.put("second", "love");
    child.put("third", "you");
    parser.put_child("text", child);
    parser.put("type", "test");
    parser.put("begin", 1);
    parser.put("end", 1);
    stringstream ss;
    boost::property_tree::json_parser::write_json(ss, parser);
    cout << ss.str() << endl;
    return 0;
}

Before run the codes, you should install boost 1.57. The codes run well in gcc 4.7, boost 1.57.The output is { "id" : 0, "text" : { "first" : "i", "second" : "love", "third" : "you" }, "type" : "test" "begin" : 1, "end" : 1, }. About boost::property_tree::ptree, you can click here. It used list<pair<key, ptree>> for saving data. So it saved unordered data, unless you called list.sort(). I hope this can help you.

0
The Dark On

No, I don't think you can. JsonCpp keeps its values in a std::map<CZString, Value>, which is always sorted by the CZString comparison. So it doesn't know the original order you added items.

0
J C Gonzalez On

As mentioned by The Dark, JsonCpp keeps its values in a std::map<CZString, Value>, which is always sorted by the CZString comparison, without keeping track neither of the original order in which you added the items nor the desired order in the output.

But you can use this "hidden feature" in your benefit. I mean, you just need that the keys in the desired order follow the "natural" order of CZString. I have a method in my JSONCPP wrapper classes that do this. The quick'n'dirty code, converted to simple function, would be something like this:

std::string sortedStr(Json::Value & value, std::vector<std::string> sortKeys) 
{
    Json::Value sortedValue;  // The JSON object to store the new (sorted) hash
    char newKey[60]; // I use C expressions, modify to C++ if you like 
    // Build new sortedValue
    int i = 0;
    for (auto & key : sortKeys) {
        sprintf(newKey, "SORTEDKEY:%03d-%s", i++, key.c_str());
        sortedValue[newKey] = value[key];
    }
    // Write to string, should be sorted on primary keys
    Json::StyledWriter w;
    std::string result = w.write(sortedValue);
    // Remove aux. tags from primary keys 
    std::size_t pos = 0;       
    while ((pos = result.find("SORTEDKEY:", pos)) != std::string::npos) {
        result.erase(pos, 14);
    }
    return result;
}

To use it, just call:

std::string sortedObjStr = sortedValue(myValue, {"first", "second", "third", "fourth"});

Note that:

  • I use this for relatively small objects (configuration data).
  • I use the "tag" SORTEDKEY, since this is not going to appear anywhere in my data. Modify according to your needs.
  • I do not check that the keys used do exist. You can add this check.
  • You can use this also to generate a restricted, ordered subset of your original object.
0
Jan On

The key-value pairs in an object will always be sorted. Json arrays are not sorted, they consists of a series of values without keys. Objects, as named collections (arrays) of key-value pairs within brackets, in an array, will retain their positions, e.g.

{
"adressen" : [
  {
     "start" : {
        "ID" : 1,
        "key" : "2352KJ25",
        "lat" : 52.157225922529967,
        "lon" : 4.5298663828345527
     }
  },
  {
     "eind" : {
        "ID" : 2,
        "key" : "2352KJ25",
        "lat" : 52.157225922529967,
        "lon" : 4.5298663828345527
     }
  }
}

ID, key, lat, lon are sorted, but start and eind are in their original positions. So, at least your first, second, third could have been

Json::Value text(Json::arrayValue);
text.append("I");
text.append("love");
text.append("you");

No need for the tags first, second and third! Maybe this helps you to find a workaround.

0
ansanes On

This is my workaround to a get an ordered json output from jsoncpp

Json::Value root;
root["*1*id"]=0;
        Json::Value text;
        text["*1*first"]="i";
        text["*2*second"]="love";
        text["*3*third"]="you";
root["*2*text"]=text;
root["*3*type"]="test";
root["*4*begin"]=1;
root["*5*end"]=1;
Json::StyledWriter writer;
string resultString=writer.write(root);
resultString=ReplaceAll(resultString,"*1*", "");
resultString=ReplaceAll(resultString,"*2*", "");
resultString=ReplaceAll(resultString,"*3*", "");
resultString=ReplaceAll(resultString,"*4*", "");
resultString=ReplaceAll(resultString,"*5*", "");
cout<<"JSON WriteTest" << endl << resultString <<endl;

with RepleceAll function defined as this

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
        size_t start_pos = 0;
        while((start_pos = str.find(from, start_pos)) != std::string::npos) {
            str.replace(start_pos, from.length(), to);
            start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
        }
        return str;
    }