3

I am new to C++ and cannot figure out how to strip some miscellaneous data from a string and then parse it as JSON.

I've ended up using the most documented JSON parser I could find - jansson. It seems excellent, although I'm stuck at the first hurdle.

My program receives a string in the following format:

5::/chat:{"name":"steve","args":[{"connection":"true"}, { "chatbody" : "I am the body" }]}

I've stripped everything outside the curly brackets with:

std::string str=message;
unsigned pos = str.find("{");
std::string string = str.substr (pos);

That leaves:

{
    "name": "steve",
    "args": [
        {
            "connection": "true"
        },
        {
            "chatbody": "I am the body"
        }
    ]
}

I'm stuck at stage one parsing this. I have converted the string to a char and then tried to use json_loads, but I don't get anything useful out...

The whole thing looks like this:

void processJson(string message)
{
    json_t *root;
    json_error_t error;
    size_t i;

    std::string str=message;
    unsigned pos = str.find("{");
    std::string str3 = str.substr (pos);

    const char * c = str.c_str();

    json_t *data, *sha, *name;

    root = json_loads(c, 0, &error);
    data = json_array_get(root, i);        
    cout << data;

    if(!json_is_object(root))
    {
      fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
    }

}

I need to get the values out, but I just get 01, 02, 03....

is_json_object just says:

error: commit data 1068826 is not an object
error: commit data 1068825 is not an object
error: commit data 1068822 is not an object

What am I doing wrong and how can I properly format this? Ultimately I'll need to iterate over an array but cannot get past this. I'm sure this is just a beginner's mistake.

-EDIT-

Trying to avoid using Boost because of a strict size requirement.

halfer
  • 19,824
  • 17
  • 99
  • 186
simonmorley
  • 2,810
  • 4
  • 30
  • 61

4 Answers4

4

You could always use an existing solution like Boost's property tree, which has a function for automatically parsing JSON files. It's literally as easy as adding these two headers:

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

And then adding this small snippet of code in, where jsonfile obviously means your filename.

boost::property_tree::ptree jsontree;
boost::property_tree::read_json(jsonfile, jsontree);

If you ever want to extract information from your JSON tree, you can do it like this, where type is the type of the data you want you extract, and insert.key.path.here is the path to your key, with each parent key separated by periods.

jsonfile.get<type>(insert.key.path.here);

In addition, I don't believe the JSON string you have there is valid. You did good removing the excess around the JSON string itself, but I believe there's a problem here:

"connection" : true,

You can check the validity of your JSON string here: http://jsonformatter.curiousconcept.com/

Freddy Pierson
  • 433
  • 2
  • 10
  • 1
    I'm to avoid the boost libraries because I'm short on space and memory - need to run this on an embedded device with on 32Mb RAM.. Hence I've tried libjson, rapidjson, jsoncpp and jansson. They're all too complicated for a newcomer to c++. – simonmorley Jul 07 '13 at 19:48
  • Try using Casablanca (REST C++ SDK) on CodePlex. – Tony Jul 08 '13 at 01:17
  • 1
    How does Casablanca help with this specific problem? – simonmorley Jul 08 '13 at 08:42
4

For JSON formatting, I've searched for a pretty print solution via C++ to no avail. Finally, I found some java code which I eventually converted to C++. Try the following for JSON formatting:

std::string formattedJson(char *json)
{
    std::string pretty;

    if (json == NULL || strlen(json) == 0)
    {
        return pretty;
    }

    std::string str     = std::string(json);
    bool        quoted  = false;
    bool        escaped = false;
    std::string INDENT  = "    ";
    int         indent  = 0;
    int         length  = (int) str.length();
    int         i;

    for (i = 0 ; i < length ; i++)
    {
        char ch = str[i];

        switch (ch)
        {
            case '{':
            case '[':
                pretty += ch;

                if (!quoted)
                {
                    pretty += "\n";

                    if (!(str[i+1] == '}' || str[i+1] == ']'))
                    {
                        ++indent;

                        for (int j = 0 ; j < indent ; j++)
                        {
                            pretty += INDENT;
                        }
                    }
                }

                break;

            case '}':
            case ']':
                if (!quoted)
                {
                    if ((i > 0) && (!(str[i-1] == '{' || str[i-1] == '[')))
                    {
                        pretty += "\n";

                        --indent;

                        for (int j = 0 ; j < indent ; j++)
                        {
                            pretty += INDENT;
                        }
                    }
                    else if ((i > 0) && ((str[i-1] == '[' && ch == ']') || (str[i-1] == '{' && ch == '}')))
                    {
                        for (int j = 0 ; j < indent ; j++)
                        {
                            pretty += INDENT;
                        }
                    }
                }

                pretty += ch;

                break;

            case '"':
                pretty += ch;
                escaped = false;

                if (i > 0 && str[i-1] == '\\')
                {
                    escaped = !escaped;
                }

                if (!escaped)
                {
                    quoted = !quoted;
                }

                break;

            case ',':
                pretty += ch;

                if (!quoted)
                {
                    pretty += "\n";

                    for (int j = 0 ; j < indent ; j++)
                    {
                        pretty += INDENT;
                    }
                }

                break;

            case ':':
                pretty += ch;

                if (!quoted)
                {
                    pretty += " ";
                }

                break;

            default:
                pretty += ch;

                break;
        }
    }

    return pretty;
}
Kona Kid
  • 41
  • 1
2

I'm not familiar with whatever JSON library you're using, but here's a few suggestions about what might be wrong.

  • size_t i is not initialized to anything, and is then passed into json_array_get.
  • json_array_get is passed the root object, which is not a JSON array, but a JSON object. In JSON terminology, root["args"] would be the array.

Of course, depending on the semantics of your JSON library, neither of this might be issues at all, but they seem like red flags to me.

David Adrian
  • 1,079
  • 2
  • 9
  • 24
  • Am not really using size_t i right now. If root["args"] is the array aspect, how can I get to that bit? According the json_is_object, it's not even a valid json object, so I just can't fathom how to get to the data. This is sooo easy in Ruby, JS, Python and a pain in the neck in c++ – simonmorley Jul 07 '13 at 19:51
  • Looking closer, your JSON is invalid. That's another reason it's not working. Run it through JSON lint to see what I mean. – David Adrian Jul 07 '13 at 21:59
  • Yeah, I can see that. I've chopped some off for SO. The real data's actually in tact and passes on jsonlint. – simonmorley Jul 07 '13 at 22:20
  • If I use root = json_loads(json, 0, &error); on the char value, I get an output of "root = 0x1001053900x1001055e00x1001058d02". Which I don't understand. Apparently this is neither a string nor an array which seems odd. I'm assuming I've got to somehow dump the data to get jansson to read it. – simonmorley Jul 07 '13 at 22:27
  • After running json_loads, do array = json_object_get(root, "args") to get the json array. Then use json_array_get(array, index) to get things out of it. – David Adrian Jul 07 '13 at 23:01
  • Just loaded jsoncpp to see if it was the data being piped in or not. It's working with jsoncpp without problem, so the issue is with my jansson configuration. Wil try your suggestion in a mo. – simonmorley Jul 07 '13 at 23:34
  • Tried that actually yesterday but it doesn't work. I don't think it's an array. It has an array in it, but it's made up of sections. I need to extract the strings and then the array to get to the data. – simonmorley Jul 08 '13 at 08:40
  • It's an array of objects, so you'll have to use the object functions on whatever you pull out of the array. Really you should pause, think about what's going on, read the docs, think about what's in your JSON object, and stop just throwing code at this. – David Adrian Jul 08 '13 at 14:23
  • Or just switch to jsoncpp and have a much better day :) Spent some time reading about jansson and I think the mem usage was going to be a problem. The documentation for jsoncpp is better too. Thanks for your help. – simonmorley Jul 08 '13 at 14:52
2

Casablanca (REST C++ SDK) has a pretty nice JSON parser which you can use even if you don't use the HTTP functionality.

You can extract the JSON parser files into a static library and link it with your existing project. The files to extract are:

src\json\json.cpp
src\json\json_parsing.cpp
src\json\json_serialization.cpp
src\utilities\asyncrt_utils.cpp

include\cpprest\json.h
include\cpprest\xxpublic.h
include\cpprest\basic_types.h
include\cpprest\asyncrt_utils.h

I can confirm this works as I've used it recently as a static library for my projects.

I also tried to use Jansson but the parser in Casablanca simply feels easier to use and has better Unicode support.

Bogdan
  • 69
  • 1
  • 4