Sunday, April 26, 2015

Building JSONs using the POCO C++ library

POCO refers to a very handy set of libraries for C++. They are cleanly written, STL-like, easy to use and most importantly, free (Boost License). I've had a small encounter with these libraries, mainly the JSON module and I'll detail, in this article, on how to build a JSON and then how to parse a JSON object manually using POCO::JSON.

For the purpose of this article I'll take a simple JSON which sort of covers all the cases (simple values, objects, arrays):
{
 "menu" : {
  "id" : "file",
  "menuitem" : [
   {
    "onClick" : "CreateNewDoc()",
    "value" : "New"
   },
   {
    "onClick" : "OpenDoc()",
    "value" : "Open"
   },
   {
    "onClick" : "CloseDoc()",
    "value" : "Close"
   }
  ],
  "value" : "File"
 }
}
In order to create a JSON object in memory, one has to do the following:
#include <Poco/JSON/Object.h>

Poco::JSON::Object::Ptr buildJSON()
{
    Poco::JSON::Object::Ptr root = new Poco::JSON::Object();
    Poco::JSON::Object::Ptr menuObj = new Poco::JSON::Object();
    menuObj->set("id", "file");
    menuObj->set("value", "File");

    //menuitem array
    Poco::JSON::Array::Ptr menuitemArray = new Poco::JSON::Array();

    //objects in array
    Poco::JSON::Object::Ptr arrObj1 = new Poco::JSON::Object();
    arrObj1->set("value", "New");
    arrObj1->set("onClick", "CreateNewDoc()");

    Poco::JSON::Object::Ptr arrObj2= new Poco::JSON::Object();
    arrObj2->set("value", "Open");
    arrObj2->set("onClick", "OpenDoc()");

    Poco::JSON::Object::Ptr arrObj3 = new Poco::JSON::Object();
    arrObj3->set("value", "Close");
    arrObj3->set("onClick", "CloseDoc()");

    menuitemArray->set(0, arrObj1);
    menuitemArray->set(1, arrObj2);
    menuitemArray->set(2, arrObj3);

    menuObj->set("menuitem", menuitemArray);

    root->set("menu", menuObj);

    return root;
}
The function above is used particularly for the example JSON. In a production environment one would not hardcode the keys as above. As you may have observed, there is heavy usage of smart pointer objects: this is done to avoid duplication of data.
The resulting JSON object can be printed using the stringify method of the Poco::JSON::Object class.

Now that we have a JSON object, we would like to check the contents. POCO has a few ways to do this: one would be to go through the values knowing the key names, the other being using iterators, without knowing the keys and what the JSON object contains.
The following snippet prints the contents of the JSON objects we have built above, using the second approach:
void printArray(Poco::JSON::Array::Ptr array);

void printObject(Poco::JSON::Object::Ptr object){
    //print the object
    Poco::JSON::Object::ConstIterator it = object->begin();
    Poco::JSON::Object::ConstIterator endit = object->end();
    while(it != endit){
        std::cout << it->first << " : ";
        if(it->second.isNumeric()){
            int val=0;
            it->second.convert<int>(val);
            std::cout << val;
        }else if(it->second.isString()){
            std::string val;
            it->second.convert<std::string>(val);
            std::cout << val;
        }else if(it->second.isBoolean()){
            bool val=0;
            it->second.convert<bool>(val);
            std::cout << val;
        }else if(it->second.isEmpty()){
            std::cout << "null";
        }else {
            //array
            try{
                Poco::JSON::Array::Ptr spArray;
                spArray = it->second.extract<Poco::JSON::Array::Ptr>();
                printArray(spArray);
                ++it;
                continue;
            }
            catch(Poco::BadCastException&){/*continue silently*/}
            //object
            try{
                Poco::JSON::Object::Ptr spObject;
                spObject = it->second.extract<Poco::JSON::Object::Ptr>();
                printObject(spObject);
                ++it;
                continue;
            }
            catch(Poco::BadCastException&){/*continue silently*/}
        }
        std::cout << std::endl;
        ++it;
    }
}

void printArray(Poco::JSON::Array::Ptr array){
    for(int i=0; i<array->size(); ++i){
        Poco::JSON::Object::Ptr spObj = array->getObject(i);
        //print object
        printObject(spObj);
    }
}

int main()
{
    Poco::JSON::Object::Ptr menuObj = buildJSON();

    //print the object
    printObject(menuObj);

    return 0;
}
There seem to be some issues with the isArray method in the Poco::Dynamic::Var class (it doesn't work). This is why I've used the try-catch blocks to get the array type. The rest of the code should be easy to understand.

No comments:

Post a Comment