Home

Awesome

cwjson - C++ JSON parser

Description

cwjson is a small, fast and safe C++ JSON parser. Currently it supports UTF8 parsing/generation, but can be upgraded to UTF16/UTF32 in the future.

Please contact me via github or my blog if you have any suggestions, questions or bug reports.

Requirements

Installation

cwjson parser doesn't require installation. Add two source files to your project and include cwjson.h to the header search path.

Documentation

Coming soon.

Memory allocations

IMPORTANT: all pointers/references returned by cwjson library point to internal data structure, you MUST NEVER delete or free them. DO NOT store them for late use, or do it with great caution. If you remove parent value from the data tree, all child pointers will be invalid.

The only exception to this rule is "clone()" method. "clone()" method makes exact copy of value with all its children and return pointer to a copy. It's your responsibility to delete that memory using C++ standard library "delete".

All cwjson linkXXXXXXXX() methods accept pointers to value and that value will be owned by cwjson and deleted in the future. linkXXXXXXXX() methods can throw an exception. It's your job to prevent memory leaks or other problems. NEVER DO the following:

You are warned!

Performance

Try to avoid using Object/Array setValue()/pushValue()/insertValue() methods. They clone values internally and hurt the performance. These methods are useful if you want to copy parts of the tree. If you need to insert large object with many children, consider using linkXXXXXXXX() methods instead.

JSON example

{
   "Image" : {
      "Width" : 800,
      "Height" : 600,
      "Title" : "View from 15th Floor",
      "32bit" : true,
      "Thumbnail": {
         "Url" : "http://www.example.com/image/481989943",
         "Height" : 125,
         "Width" : 100
      },
      "IDs" : [116, 943, 234, 38793]
   }
}

Basic parsing and data access

If you are parsing JSON object with known structure, you can use basic data access methods. It's easy to use and safe.

  // Load, receive or somehow get the JSON data into 
  // std::string or char * buffer
  
  try
  {
     cwjson::Root root(buffer);

     cwjson::Object    &image = root.getObject().getObject("Image");
     const std::string &title = image.getString("Title").getValueStr();
     int                width = (int)image.getNumber("Width").getValue();
     int                height = (int)image.getNumber("Height").getValue();

     std::cout << title << " " << width << "x" << height << std::endl; 

     cwjson::Array &ids = image.getArray("IDs");
     for (int i = 0; i < ids.childCount(); ++i)
        std::cout << ids.getNumber(i).getValue() << " ";

     std::cout << std::endl;
  }
  catch (cwjson::JsonError &e)
  {
     // Parsing or data access exception. cwjson will throw an exception 
     // if it can't find value with specified name, array index is out of bounds
     // or trying to cast the value to incorrect type.

     std::cout << "Json Exception: " << e.what() << std::endl;
  }

Program will print the following:

  View from 15th Floor 800x600
  116 943 234 38793

null value handling

JSON data can contain "null" values. Let assume that "Thumbnails" object can be null. Trying to access it, following code

  std::cout << image.getObject("Thumbnail").getString("Url").getValueStr();

will throw an exception "value is not an object" because "Thumbnail" is not an object. Use "isNull" method to check the value.

  if (!image.isNull("Thumbnail"))
     std::cout << image.getObject("Thumbnail").getString("Url").getValueStr();

JSON generation

Let compose JSON example.

  try
  {
     cwjson::Root root;

     cwjson::Object &rootObject = root.createRootObject();
     cwjson::Object &image      = rootObject.createObject("Image");
     image.setNumber("Width", 800);
     image.setNumber("Height", 600);
     image.setString("Title", "View from 15th Floor");
     image.setBoolean("32bit", true);

     cwjson::Object &thumbnail = image.createObject("Thumbnail");
     thumbnail.setString("Url", "http://www.example.com/image/481989943");
     thumbnail.setNumber("Width", 125);
     thumbnail.setNumber("Height", 100);

     cwjson::Array &ids = image.createArray("IDs");
     ids.pushNumber(116);
     ids.pushNumber(943);
     ids.pushNumber(234);
     ids.pushNumber(38793);

     std::ostringstream out;
     root.print(out, true);  // "true" means formated output
  }
  catch (cwjson::JsonError &e)
  {
     std::cout << "Json Exception: " << e.what() << std::endl;
  }

At the program end "out" stringstream will contain formated JSON object string.

JSON object tree traversal

If you don't know the JSON data structure, you can use generic value access methods to traverse the tree. It's less safe comparing to normal data access, because you need to be careful with data pointers.

JSON data tree is built from the six main objects - Object, Array, String, Number, Boolean and Null. All these objects have same base class Value. You can use Value object methods to traverse the tree. Each Value object is a tree node and have parent, children and siblings. Use getType() method to find out the type of the object and appropriate method to convert Value object to derived class object.

Recursively walk JSON data tree:

  void recurseValue(cwjson::Value *value)
  {
     switch (value->getType())
     {
        case cwjson::TypeRoot:
           recurseValue(value->firstChild());
           break;

        case cwjson::TypeObject:
        case cwjson::TypeArray:
        {
           cwjson::Value *item = value->firstChild();
           while (item)
           {
              recurseValue(item);
              item = item->nextSibling();
           }
        }
        break;

        case cwjson::TypeString:
           std::cout << value->getNameStr() << " : " << value->toString().getValueStr() << std::endl;
           break;

        case cwjson::TypeNumber:
           std::cout << value->getNameStr() << " : " << value->toNumber().getValue() << std::endl;
           break;

        ...
        // Boolean and Null types
        ...
     }
  }

  int main(int argc, char* argv[])
  {
     // Load, receive or somehow get the JSON data into 
     // std::string or char * buffer

     try
     {
        cwjson::Root root(buffer);
        recurseValue(&root);
     }
     catch (cwjson::JsonError &e)
     {
        // Parsing exception. cwjson also throws an exception if you try cast Value to 
        // incorrect type.

        std::cout << "Json Exception: " << e.what() << std::endl;
     }
  }

There is another method to traverse the tree. Create a new class derived from cwjson::Visitor, overload methods and pass class instance to traverse() method. traverse() method will recursively walk all Value children and call your overloaded methods. If value type is ether String, Number, Boolean or Null appropriate visit() method will be called. If value type is Object or Array, enter() method is called before traversing children and exit() method is called after traversing is done. Return "true" to continue traversing. Return "false" in enter() to skip all Object/Array children. Return "false" in visit()/exit() methods to skip all remaining parent children.

Following example produces same result, but code is safe and simple.

  class TestVisitor : public cwjson::Visitor
  {
  public:
     bool visit(const cwjson::String &value) 
     { 
        std::cout << value.getNameStr() << " : " << value.toString().getValueStr() << std::endl;
        return true;
     }

     bool visit(const cwjson::Number &value) 
     {
        std::cout << value.getNameStr() << " : " << value.toNumber().getValue() << std::endl;
        return true; 
     }
  };

  int main(int argc, char* argv[])
  {
     // Load, receive or somehow get the JSON data into 
     // std::string or char * buffer

     try
     {
        cwjson::Root root(buffer);
        root.traverse(TestVisitor());
     }
     catch (cwjson::JsonError &e)
     {
        // Parsing exception.
        std::cout << "Json Exception: " << e.what() << std::endl;
     }
  }

Changing existing JSON tree

Simply get reference or pointer to an existing value and use setName()/setValue()/removeValue() methods. You can change JSON data in any way you want, for example - parse input, modify data, generate JSON object string.

Author

Sergej Kravcenko
http://www.codewaves.com
sergej@codewaves.com