JsonCpp

This course describes the JsonCpp library (also called jsoncpp and json-cpp), JsonCpp is probably the most popular library for working with JSON databases in C++. It can parse and save databases, and provides an extensive library for accessing and manipulating their members. JsonCpp works with both files and strings.

For a library which exists for years, JsonCpp is surprisingly poorly documented. You can find either a very simple example of usage, or a computer-generated list of all classes and methods.

JSON
JSON (JavaScript Object Notation) is an ASCII format for representing various data structures. It is pretty language-independent, machine-independent, simple, and easy readable by both humans and computers.

JSON is fully described at json.org.

In brief, a JSON value can be one of the following (see json.org for full details):

\" - quote   \\ - backslash    \/ - slash    \n - newline    \t - tabulation    \r - carriage return    \b - backspace    \f - form feed    \uxxxx, where x is a hexadecimal digit - any 2-byte symbol    [1, 2, 3, "Hello world\n", true]    key : value where key is a string. Example:    {"foo":1, "bar":2, "baz":3.14, "hello world":[1,2,3]}
 * null
 * true or false
 * A number.
 * Numbers cannot start with 0 (or -0) followed by another digit (i. e. 0 and 0.14 are valid numbers, but 03, 012.34, and -015 are not).
 * If a number contains a decimal point, it should also contain at least one digit both before and after the decimal point (i. e. numbers 15. and .15 are invalid).
 * A string. Strings are enclosed in double quotes and may contain the following escape sequences:
 * An array. It is denoted by a comma-separated list of any values in square brackets. Example:
 * An object, also called associative array, keyed list etc. It is denoted by comma-separated list of pairs in braces. A pair has a form

Whitespaces can be inserted between any tokens.

Array elements are accessed by their number, while object elements are accessed by key. Arrays and objects can be empty. Arrays and objects can recursively contain another arrays or objects.

While strict JSON syntax does not allow any comments, JsonCpp allows both C-style and C++-style comments. Strictly, the root value should be only array or object, but JsonCpp allows it to have any type.

As for February 2016, there are totally hundreds of libraries for parsing and generating JSON on 62 languages., including 22 different libraries for C++.

JsonCpp is probably the most popular C++ library. Another popular library is rapidjson, which is very fast.

With apt-get
The easiest way to use it from Ubuntu or another flavor of Debian Linux, is to install is as: sudo apt-get install libjsoncpp-dev

One drawback is that it will install the version 0.6.0. The last version is 1.7.0.

To use JsonCpp, you need to include: #include 

To compile a file, add flag -ljsoncpp

The header files will be installed to /usr/include/jsoncpp/json. In case you are curious, the libraries will be most probably installed to /usr/lib/x86_64-linux-gnu (but you hardly need their location). If you want to discover, try: ls /usr/lib/*/*jsoncpp* ls /usr/lib/*jsoncpp*

With amalgamated source
To use JsonCpp with amalgamated source, you don't need to download or make any binary files. You will have a single cpp and two .h files which you should include into your projects. These files will be system-independent.


 * Download and unzip the source from the official repository. Go to the directory.

python amalgamate.py
 * Run

It will create three files dist/jsoncpp.cpp, the source file to be added to your project dist/json/json.h, the correspondent header file dist/json/json-forwards.h, which contains forward declarations of JSON types.

You don't need anything except these three files.

With cmake
sudo apt-get install cmake mkdir -p build cd build cmake -DCMAKE_BUILD_TYPE=release -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" .. make
 * Download and unzip the source from the official repository. Go to the directory.
 * Install cmake. Under Ubuntu or another flavour of Debian Linux:
 * Create the build directory and enter it:
 * Run the cmake command
 * Run the make command

Under Unix, it will create the file src/lib_json/libjsoncpp.a in your build directory. The include files will be in the ../include/json. Install the files (make install might help), and use #include  and -ljsoncpp

With MS Visual Studio

 * Download and unzip the source from the official repository.
 * In this unzipped source tree, under makefiles/msvc2010 (for MS Visual Studio 2010) or vs71 (MS Visual Studio 2003) you will find several Visual Studio project files which you can up convert and build.

Example
Install JsonCpp.

Create file alice.json with the following contents:

Create file alice.cpp with following contents:

Compile it: g++ -o alice alice.cpp -ljsoncpp

Then run it: ./alice

You will hopefully receive the following:

Conventions and restrictions of JsonCpp
Everything is in the Json namespace.

Names of classes and other types start use upper CamelCase notation (capitalize first letter of each word). Examples: Int, ArrayIndex, ValueType. Names of member functions, fields, enum values use lower camelCase notation (capitalize first letter of each word except the first word). Examples: stringValue, isInt, size.

JsonCpp does extensive validity checking. If an operation is invalid, it throws the std::runtime_error exception with the relevant message.

JsonCpp stores each number as either 64-bit integer (long long int or __int64), or 64-bit unsigned integer (unsigned long long int or unsigned __int64), or double.

An array or an object may contain at most $$2^{32}-1$$ elements. A sring may contain at most $$2^{32}-1$$ characters. An object key may contain at most $$2^{30}-1$$ characters.

Auxillary types
JsonCpp provides several auxillary types.

The following types are defined in config.h :
 * Int - defined as int
 * UInt - defined as unsigned int
 * Int64 - a 64-bit signed integer, defined as __int64 for Microsoft Visual Studio, otherwise long long int
 * UInt64 - a 64-bit unsigned integer, defined as unsigned __int64 for Microsoft Visual Studio, otherwise unsigned long long int
 * LargestInt - a largest possible signed integer, defined as Int64
 * LargestUInt - a largest possible signed integer, defined as UInt64

ArrayIndex is a type for array indices. It is defined as unsigned int, which means that an array or object may contain at most $$2^{32}-1$$ items.

ValueType is an enum describing a type of JSON value. It is defined as:

Input/output
The simplest way to do input/output is via operator< >. The program below reads a JSON value from standard input and writes it to standard output. In case of syntax error, operator<< throws runtime_error exception.

For example:

For reading this way from a string, or writing to a string, you may use std::istringstream and std::ostringstream respectively, but there are alternative ways to do it.

toStyledString
The method toStyledString converts any value to a formatted string. Its declaration is:

Json::Reader
Another, and more robust, way to read JSON values is via the Reader class. Its most useful public methods are (here and below, comments are mine, and the order of methods is also mine):

Example:

Json::Writer
Unlike Reader, the class Writer is abstract. There are two classes which implement it:
 * FastWriter produces unformatted, non-human-readable documents. Everything is written in a single line.
 * StyledWriter produces formatted, human-readable documents, similar to operator<<, but with less indentation and without empty lines.

FastWriter has the following public methods (default constructor and destructor are not shown): StyledWriter has the following public method (default constructor and destructor are not shown):

Finally, there is also StyledStreamWriter class, for writing to streams. It is directly called by operator<<. This class is not a descendant of Writer or any other class. Its public methods are: StyledStream is not very useful, as operator<< is more convenient. You may want to use it if you want non-standard indentation.

Example:

Examining the attributes
The following method of Value returns some info about it:

isInt, isInt64, isUInt, isUInt64 return true only if all the following conditions satisfied:
 * the type is numeric (int, uint, or real)
 * if the type is real, the value should have zero fractional part
 * the value should be within the range of the given type (Int, Int64, UInt, UInt64 respectively)

isDouble and isNumeric currently return true if the type is int, uint, or double.

isIntegral always returns true for an int or uint. For a real value, it returns true if the value has zero fractional part, and within the range of Int64 or UInt64. For all other types, it returns false.

is... functions are not backwards-compatible. In earlier versions, isInt and isUInt just checked the type, isInt64 and isUInt64 did not exist, isArray and isObject also returned true for null value, isIntegral also returned true for booleans etc.

Example:

Getting the value
To get the numerical, boolean, or string value itself, the class Value provides following methods:

Some of these methods may throw std::runtime_exception. A simple rule: if isFoo returns true, then it is safe to call asFoo, but the opposite is not neccessarily true.

Another rule, it is always safe to call
 * asString for string
 * asLargestInt for int
 * asLargestUInt for uint
 * asFloat or asDouble for any number (int, uint, or real)
 * asBool for boolean

Below are the details.

The methods asInt, asUInt, asInt64, asUInt64, asLargestInt, asLargestUInt do the following:
 * If the original value is numeric, check if it is within the range of the destination type. If not, throw std::runtime_error. Then cast the value to the destination type. The casting is plain, so the real value of 3.9, when sent to asInt, becomes 3.
 * If the original value is boolean or null, return 1 for true, 0 for false, and 0 for null.
 * For strings, arrays, and objects, throw std::runtime_error.

The methods asFloat and asDouble do the following:
 * If the original value is numeric, cast it to float or double.
 * If the original value is boolean or null, return 1.0 for true, 0.0 for false, and 0.0 for null.
 * For strings, arrays, and objects, throw std::runtime_error.

The method asBool accepts anything.
 * false, null, 0, 0.0, or empty string/array/object is converted to false
 * true, non-zero number, or non-empty string/array/object is converted to true

The method asString is robust, slow, and high level. It returns stl::string. It correctly treats strings with zero characters. It accepts everything except arrays and pointers. For a null, the method returns ""; for a boolean, it returns "true" or "false"; for a number, it returns their string representation. For arrays and objects, it throws std::runtime exception.

The method asCString is the fast, low-level method. It accepts only strings (otherwise it throws std::runtime exception), and directly returns the C-style string which is stored internally. The method doesn't allocate anything. Don't call free or delete[] on the returned pointer! You should keep in mind two things:
 * Since C-style strings should not contain zero characters, the method is only suitable for strings without zero characters.
 * For empty strings, it sometimes returns 0.

If you need a C-style string, but don't want to be confused by zero characters, newer versions of JsonCpp add the following method:

This method stores the pointer to the first character to *begin, stores the pointer to the final zero character to *end, and returns true. For non-strings and sometimes for empty strings, it returns false.

Example :

Constructors, assignment and comparison
The class Json::Value provides following constructors:

The first constructor creates null, false, 0, 0.0, or empty string/array/object. The other constructos are self-explanatory.

Assignment, swap, and the fool set of comparison operators are also provided (as methods).

Methods for arrays
Arrays have their own methods. These methods also work for null.

Some of them are similar to C++ STL's vectors: Note that ArrayIndex is defined as unsigned int.

resize changes the array size by either removing last values or appending null values.

If operator[] receives a negative index, it throws the std::runtime_error exception. If it receives index which is equal to or greater than the current size,
 * non-constant operator[] appends index-size+1 null values, then returns the last value
 * constant operator[] returns the null value

To append a value, use append: This is similar to STL's vector::push_back. In other words, foo.append(bar) is equivalent to foo[foo.size]=bar.

The method get returns index-th element, or defaultValue if index is greater or equal to size: Note that it returns value NOT by reference, so calling this method may be very expensive.

To check validity of index, you may want to use isValidIndex: This is not very useful, as value.isValidIndex(index) is equivalent to index<value.size.

You can also remove one value with removeIndex: No miracles, this method takes linear time. If i is greater or equal to size, it returns false.

Calling array methods for null value
If foo is null, the methods above treat it as an empty array:
 * foo.empty returns true
 * foo.size returns 0
 * foo.clear does nothing
 * foo.resize(0) does nothing
 * foo.resize(size) for positive size transforms foo into an array of size nulls.
 * foo.operator[](i) (non-constant) transforms foo into an array of i+1 nulls, then returns the last value
 * foo.operator[](i) (constant) returns the null value
 * foo.isValidIndex(i) always returns false
 * foo.get(index, defaultValue) always returns defaultValue
 * foo.append(bar) makes foo an array of one element, which equals to bar
 * foo.removeIndex always returns false

Calling array methods for other types
The metods clear, empty and size also work for objects.

Other than this, calling any of the methods above for something which is neither array nor null is pretty useless. They either return something trivial or throw the std::runtime_error exception:
 * foo.empty returns false, unless foo is an empty object
 * foo.size returns 0, unless foo is an object
 * foo.clear throws the std::runtime_error exception, unless foo is an object
 * resize, append, get, operator[] throw the std::runtime_error exception
 * isValidIndex always returns false
 * removeIndex always returns false

Methods for objects
Objects have their own methods. These methods also work for null.

Some of them are similar to C++ STL's maps: These are self-explanatory. For operator[], if the key does not exist, non-constant operator[] inserts the (key, null) pair and returns the reference to this null, while constant operator[] just returns the reference to some null.

The following methods take a key as either C++ string, C string, or a pair of pointers specifying beginning and end of the string. The last form does not exist in old versions of JsonCpp. It is useful, for example, if a string contains zero characters.

The method isMember checks whether there exists a member with given key:

The method removeMember removes an element. The first two forms return the removed value NOT by reference, which may be pretty expensive.

To iterate through the members of an object, you need the full list of their keys. This is performed by getMemberNames: Value::Members is defined as:

The method get returns value for the given key, or, in its absence, defaultValue. Just like with arrays, it returns value NOT by reference, so calling this method may be very expensive.

The method find exists only in newer versions. It receives the key in (begin, end) form and returns pointer to the found value. If not found, it returns NULL pointer.

Calling object methods for null value
The methods above treat null value as an empty object:
 * clear does nothing
 * size returns 0
 * empty returns true
 * non-constant operator[] transform the value into one-element object, with given key and null value
 * constant operator[] returns null
 * Older versions of removeMember just return null, its newer versions just return false.
 * getMemberNames returns empty vector of strings
 * get returns defaultValue
 * find returns NULL

Calling object methods for other types
The methods clear, size, empty work also for arrays. Other than this, for a value which is neither null nor an object, the methods above return trivial value or throw the std::runtime_error exception.

Examples of work with objects and arrays
Creating a complex structure:

Recursive function for printing any value (of course, it already exists, but we implement it from scratch):

Iterators
Iterators have types Json::Value::iterator and Json::Value::const_iterator. They are bidirectional, but not random. The methods of Json::Value are

Only arrays and objects have non-trivial iterators. If foo is neither array not an object, then foo.begin==foo.end.

Iterators have the full set of operators: incrementing and decrementing (postfix and prefix ++ and --), comparison to equality and inequality, assigment, default constructor and copy constructor. Iterators are not random, so adding an integer number to iterator is not possible. Subtracting iterator from another iterator is possible, but takes linear time.

If it is an iterator to an array, then *it is a reference its element. If it is an iterator to an object, then *it NOT a reference to the (key, value) pair. It is a reference to the value itself.

operator-> is avaliable only in newer versions.

In older versions, casting from const_iterator to iterator is not possible, while the corresponding assignment is possible. For example, if val is not const, we cannot write

Example: will print:

To receive key (for iterator to object) or index (for iterator to array), Json::Value::iterator and Json::Value::const_iterator have three methods:

As explained in comments, key returns the key for an object element or the index for an array element; index returns the index for an array element, otherwise UInt(-1); memberName returns the key for an object element, otherwise empty string ("").

Example:

This works for newer version only. This prints:

For an older version, we cannot initialize Json::Value::const_iterator as Json::Value::iterator, and there is no operator->. Therefore, we should write:

Teacher

 * Zadrali : Author of the course.The teach is not an author, nor a contributor, of the library.