31

I have a C++ application that uses jsoncpp to decode a JSON string. I have created the following function but it only shows me the top level objects...

How do I get it to dump the entire object list?

--Function--

SaveJSON( json_data ); 

bool CDriverConfigurator::PrintJSONTree( Json::Value & root, unsigned short depth /* = 0 */) 
{
    printf( " {type=[%d], size=%d} ", root.type(), root.size() ); 

    if( root.size() > 0 ) {
        for( Json::ValueIterator itr = root.begin() ; itr != root.end() ; itr++ ) {
            PrintJSONTree( itr.key(), depth+1 ); 
        }
        return true;
    }

    // Print depth. 
    for( int tab = 0 ; tab < depth; tab++) {
        printf( "-"); 
    }
    
    if( root.isString() ) {
        printf( " %s", root.asString().c_str() ); 
    } else if( root.isBool() ) {
        printf( " %d", root.asBool() ); 
    } else if( root.isInt() ) {
        printf( " %d", root.asInt() ); 
    } else if( root.isUInt() ) {
        printf( " %d", root.asUInt() ); 
    } else if( root.isDouble() ) {
        printf( " %f", root.asDouble() ); 
    }
    else 
    {
        printf( " unknown type=[%d]", root.type() ); 
    }


    printf( "\n" ); 
    return true;
}

--- Input ----

{
   "modules":[
      {
         "config":{
            "position":[
               129,
               235
            ]
         },
         "name":"Modbus Task",
         "value":{
            "DeviceID":"This is the name",
            "Function":"01_READ_COIL_STATUS",
            "Length":"99",
            "Scan":"111",
            "Type":"Serve"
         }
      },
      {
         "config":{
            "position":[
               13,
               17
            ]
         },
         "name":"Modbus Connection",
         "value":{
            "Baud":"9600",
            "timeout":"2.5"
         }
      },
      {
         "config":{
            "position":[
               47,
               145
            ]
         },
         "name":"Modbus Device",
         "value":{
            "DeviceID":"55"
         }
      },
      {
         "config":{
            "position":[
               363,
               512
            ]
         },
         "name":"Function Something",
         "value":{

         }
      },
      {
         "config":{
            "position":[
               404,
               701
            ]
         },
         "name":"Function Something",
         "value":{

         }
      }
   ],
   "properties":{
      "Blarrg":"",
      "description":"",
      "name":"Modbus"
   },
   "wires":[
      {
         "src":{
            "moduleId":1,
            "terminal":"modbus.connection.output"
         },
         "tgt":{
            "moduleId":2,
            "terminal":"modbus.connection.input"
         }
      },
      {
         "src":{
            "moduleId":2,
            "terminal":"modbus.device.output"
         },
         "tgt":{
            "moduleId":0,
            "terminal":"modbus.device.output"
         }
      },
      {
         "src":{
            "moduleId":3,
            "terminal":"dataOut"
         },
         "tgt":{
            "moduleId":4,
            "terminal":"dataIn"
         }
      },
      {
         "src":{
            "moduleId":3,
            "terminal":"dataIn"
         },
         "tgt":{
            "moduleId":0,
            "terminal":"data1"
         }
      }
   ]
}

--Output--

{type=[7], size=3} {type=[4], size=0} - modules
{type=[4], size=0} - properties
{type=[4], size=0} - wires 
david
  • 1,311
  • 12
  • 32
Steven Smethurst
  • 4,495
  • 15
  • 55
  • 92

5 Answers5

35

You have some errors related to seemingly not having a great handle on recursion or the key->value nature of JSON and how that relates to the library you're using. I haven't tested this code at all, but it should work better.

void CDriverConfigurator::PrintJSONValue( const Json::Value &val )
{
    if( val.isString() ) {
        printf( "string(%s)", val.asString().c_str() ); 
    } else if( val.isBool() ) {
        printf( "bool(%d)", val.asBool() ); 
    } else if( val.isInt() ) {
        printf( "int(%d)", val.asInt() ); 
    } else if( val.isUInt() ) {
        printf( "uint(%u)", val.asUInt() ); 
    } else if( val.isDouble() ) {
        printf( "double(%f)", val.asDouble() ); 
    }
    else 
    {
        printf( "unknown type=[%d]", val.type() ); 
    }
}

bool CDriverConfigurator::PrintJSONTree( const Json::Value &root, unsigned short depth /* = 0 */) 
{
    depth += 1;
    printf( " {type=[%d], size=%d}", root.type(), root.size() ); 

    if( root.size() > 0 ) {
        printf("\n");
        for( Json::Value::const_iterator itr = root.begin() ; itr != root.end() ; itr++ ) {
            // Print depth. 
            for( int tab = 0 ; tab < depth; tab++) {
               printf("-"); 
            }
            printf(" subvalue(");
            PrintJSONValue(itr.key());
            printf(") -");
            PrintJSONTree( *itr, depth); 
        }
        return true;
    } else {
        printf(" ");
        PrintJSONValue(root);
        printf( "\n" ); 
    }
    return true;
}
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • 1
    @Steven smethurst: What's really amusing is that I've never even looked at that library before in my life. :-) – Omnifarious May 11 '11 at 02:00
  • @cegprakash: Why did you remove the 'reference' tags? – Omnifarious Jan 05 '13 at 22:57
  • How would I do this if I can't use Json::ValueIterator? – Eric Apr 05 '17 at 16:17
  • @EricGeordi - Well, I have to ask, why can't you? – Omnifarious Apr 05 '17 at 16:25
  • @Omnifarious It appears that the version of the jsoncpp library I am using currently doesn't support ValueIterator (doesn't compile). Perhaps I just need to update our library, but wanted to see if there was a way to avoid that. – Eric Apr 05 '17 at 16:58
  • @EricGeordi - I don't know anything about the library. Maybe it has a way to do indexed access of the sub-values? Perhaps using array notation (i.e. `root[i]` or `root.values[i]`)? Then you could use a counting `for` loop instead. Presumably there's a way to find out how many sub-values there are. I don't know JsonCpp at all. I just know recursion and so framed my answer in terms of how the original poster described the API. :-) – Omnifarious Apr 05 '17 at 17:18
  • @EricGeordi - Looking at the documentation, it appears I'm right. But, getting the 'key' values is going to be a huge pain. Look at `getMemberNames()`. Then you get an array of all the keys that `iter.key` would otherwise fetch. Then you can use `value[key]` to get the associated value. I'm not going to write it out for you. You should be able to figure it out. – Omnifarious Apr 05 '17 at 17:25
  • Yeah, that's the route I'm taking here. Thanks! – Eric Apr 05 '17 at 18:01
  • 2
    Just FYI for anyone curious, an approach going the getMemberNames() route can be found here: https://en.wikibooks.org/wiki/JsonCpp#Calling_object_methods_for_other_types – Eric Apr 05 '17 at 18:51
  • Someone has suggested (with an edit) that I replace `Json::ValueIterator itr` with `Json::Value::const_iterator itr`. I have no idea why. Perhaps someone could enlighten me? Maybe I should just change the type to `auto` and make it require a C++11 compiler? – Omnifarious Jun 28 '17 at 18:40
  • @Omnifarious: You have `const Json::Value &root` and hence `root.begin()` returns `Json::Value::const_iterator`. If you removed the `const` type qualifier, then `root.begin()` would return `Json::ValueIterator`. – desowin Jul 20 '17 at 13:58
  • @desowin, has JSonCPP changed its interface that much? It seems drastically different to have the Iterator be a top-level class vs. a subclass of Value. It's more consistent with the standard library style, but it seems like it would break clients a lot. – Omnifarious Jul 20 '17 at 15:06
  • 2
    @Omnifarious: Actually, `Json::ValueIterator` is a typedef. There is also `Json::ValueConstIterator` typedef. So maybe the proper change is to replace `Json::ValueIterator` with `Json::ValueConstIterator`. GCC suggests `Json::Value::const_iterator` in error message so most likely it was the reason for the suggested edit. – desowin Jul 21 '17 at 07:12
  • @desowin - Ahh, actually no, the nested class version is better. More consistent with the standard library. I was just reluctant because it seemed like a drastic interface change. Now that I know the 'right' way has been there all along, I'll fix it. – Omnifarious Jul 21 '17 at 07:54
  • Is it unintuitive that it's itr.key() not itr->key(), when itr is supposed to be modeled after a pointer? – John Jiang Dec 11 '19 at 22:09
9

If you are just looking to print out the Json::Value, there's a method for that:

Json::Value val;
/*...build the value...*/
cout << val.toStyledString() << endl;

Also, you may want to look into the Json::StyledWriter, the documentation for it is here. I believe it print a human friendly version. Also, Json::FastWriter, documentation here, prints a more compact form.

JaredC
  • 5,150
  • 1
  • 20
  • 45
2

This is a good example that can print either json objects and object member (and it's value) :

Json::Value root;               // Json root
Json::Reader parser;            // Json parser

// Json content
string strCarPrices ="{ \"Car Prices\": [{\"Aventador\":\"$393,695\", \"BMW\":\"$40,250\",\"Porsche\":\"$59,000\",\"Koenigsegg Agera\":\"$2.1 Million\"}]}";

// Parse the json
bool bIsParsed = parser.parse( strCarPrices, root );
if (bIsParsed == true)
{
    // Get the values
    const Json::Value values = root["Car Prices"];

    // Print the objects
    for ( int i = 0; i < values.size(); i++ )
    {
        // Print the values
        cout << values[i] << endl;

        // Print the member names and values individually of an object
        for(int j = 0; j < values[i].getMemberNames().size(); j++)
        {
            // Member name and value
            cout << values[i].getMemberNames()[j] << ": " << values[i][values[i].getMemberNames()[j]].asString() << endl;
        }
    }
}
else
{
    cout << "Cannot parse the json content!" << endl;
}

The output :

{
        "Aventador" : "$393,695",
        "BMW" : "$40,250",
        "Koenigsegg Agera" : "$2.1 Million",
        "Porsche" : "$59,000"
}
Aventador: $393,695
BMW: $40,250
Koenigsegg Agera: $2.1 Million
Porsche: $59,000
0

There is an easy way to iterate over all fields in json::value. I omitted the printf stuff.

#include "cpprest/json.h"
#include "cpprest/filestream.h"

using web::json::value;
using std::wstring;

static void printOneValue(const wstring &key, const double &value);
static void printOneValue(const wstring &key, const bool &value);
static void printOneValue(const wstring &key, const int &value);
static void printOneValue(const wstring &key, const wstring &value);
static void printOne(const wstring &key, const value &v, _num level);
static void printTree(const value &v);

static void printTree(const value &v)
{
    if(!v.is_object())
        return;

    try
    {
        printOne(wstring(), v, 0);
    }
    catch(...)
    {
        // error handling
    }
}

static void printOne(const wstring &key, const value &v, _num level)
{
    switch(v.type())
    {
    case value::value_type::Number:
        if(v.is_double())
            printOneValue(key, v.as_double());
        else
            printOneValue(key, v.as_integer());
        break;
    case value::value_type::Boolean:
        printOneValue(key, v.as_bool());
        break;
    case value::value_type::String:
        printOneValue(key, v.as_string());
        break;
    case value::value_type::Object:
        for(auto iter : v.as_object())
        {
            const wstring &k = iter.first;
            const value &val = iter.second;
            printOne(k, val, level+1);
        }
        break;
    case value::value_type::Array:
        for(auto it : v.as_array())
        {
            printOne(key, it, level+1);
        }
        break;
    case value::value_type::Null:
    default:
        break;
    }
}

static void printOneValue(const wstring &key, const wstring &value)
{
    // process your key and value
}

static void printOneValue(const wstring &key, const int &value)
{
    // process your key and value
}

static void printOneValue(const wstring &key, const double &value)
{
    // process your key and value
}

static void printOneValue(const wstring &key, const bool &value)
{
    // process your key and value
}
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. Please read this [how-to-answer](http://stackoverflow.com/help/how-to-answer) for providing quality answer. – thewaywewere Jun 18 '17 at 13:02
  • 1
    The example code here isn't even based on JsonCpp, but a completely different library! So this is an answer to a completely different question. – Tom Quarendon Nov 09 '17 at 13:36
0

Given member names, get the values

// Print all items under data1 value
vector<string> memberNames = root["test1"]["data1"].getMemberNames();
for (const string& mn : memberNames)
{
    cout << "[" << mn << "]:" << "[" << root["test1"]["data1"].get(mn, "None") << "]" << endl;
}
HandyManDan
  • 148
  • 1
  • 6