Jump to content

JsonCpp

100% developed
From Wikibooks, open books for an open world

Foreword

[edit | edit source]

This book 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.

I am not an author, nor a contributor, of the library.

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):

  • null
  • true or false
  • A number.
    • Numbers cannot start with 0 (or -0) followed by another digit (e. g. 0 and 0.14 are valid numbers, while 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 (e. g. numbers 15. and .15 are invalid).
  • A string. Strings are enclosed in double quotes and may contain the following escape sequences:
   \" - 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
  • An array. It is denoted by a comma-separated list of any values in square brackets. Example:
   [1, 2, 3, "Hello world\n", true]
  • 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
   key : value

where key is a string, value is any JSON value. Example:

   {"foo":1, "bar":2, "baz":3.14, "hello world":[1,2,3]}

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, and requires the root value to be array or object, JsonCpp allows both C-style and C++-style comments, and allows the root value to be of 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++.[1]

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

Installation and running

[edit | edit source]

With apt or apt-get

[edit | edit source]

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

(You may use apt instead of apt-get.)

One drawback is that it will not install the last version. For Ubuntu 18.04.1, the version is 1.7.4, while the last version as per August 2018 is 1.8.4. The problem was much worse for 14.04, which installed 0.6.0, while the last version as per February 2016 was 1.7.4.

To use JsonCpp, include:

   #include <jsoncpp/json/json.h>

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

[edit | edit 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.

  • Run
   python amalgamate.py

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

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

(You may use -j flag to parallelize the building, e.g. make -j 4.)

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 <jsoncpp/json/json.h>

and

   -ljsoncpp

With MS Visual Studio

[edit | edit source]
  • 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

[edit | edit source]

Install JsonCpp.

Create file alice.json with the following contents:

{
    "book":"Alice in Wonderland",
    "year":1865,
    "characters":
    [
        {"name":"Jabberwock", "chapter":1},
        {"name":"Cheshire Cat", "chapter":6},
        {"name":"Mad Hatter", "chapter":7}
    ]
}

Create file alice.cpp with following contents:

#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h> // or jsoncpp/json.h , or json/json.h etc.

using namespace std;

int main() {
    ifstream ifs("alice.json");
    Json::Reader reader;
    Json::Value obj;
    reader.parse(ifs, obj); // reader can also read strings
    cout << "Book: " << obj["book"].asString() << endl;
    cout << "Year: " << obj["year"].asUInt() << endl;
    const Json::Value& characters = obj["characters"]; // array of characters
    for (int i = 0; i < characters.size(); i++){
        cout << "    name: " << characters[i]["name"].asString();
        cout << " chapter: " << characters[i]["chapter"].asUInt();
        cout << endl;
    }
}

Compile it:

   g++ -o alice alice.cpp -ljsoncpp

Then run it:

   ./alice

You will hopefully receive the following:

Book: Alice in Wonderland
Year: 1865
    name: Jabberwock chapter: 1
    name: Cheshire Cat chapter: 6
    name: Mad Hatter chapter: 7

Conventions and restrictions of JsonCpp

[edit | edit source]

Everything is in the Json namespace.

Names of classes and other types 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 performs extensive validity checking. If an operation is invalid, it throws the std::runtime_error exception with 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. Below, we are going to call these int, uint, and real respectively.

An array or an object may contain at most elements. A string may contain at most characters. An object key may contain at most characters.

Auxiliary types

[edit | edit source]

JsonCpp provides several auxiliary 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 unsigned 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 items.

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

   enum ValueType
   {
      nullValue = 0, ///< 'null' value
      intValue,      ///< signed integer value
      uintValue,     ///< unsigned integer value
      realValue,     ///< double value
      stringValue,   ///< UTF-8 string value
      booleanValue,  ///< bool value
      arrayValue,    ///< array value (ordered list)
      objectValue    ///< object value (collection of name/value pairs).
   };

Input/output

[edit | edit source]

The simplest way to do input/output is via operator<< and 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.

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;
 
int main() {
    Json::Value val;
    cin >> val;
    cout << val;
}

For example:

$ g++ -o copy-json copy-json.cpp -ljsoncpp
$ echo '{"a":[1,2],"b":[3,4]}' | ./copy-json

{
        "a" : [ 1, 2 ],
        "b" : [ 3, 4 ]
}

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()

[edit | edit source]

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

std::string toStyledString() const;

Json::Reader

[edit | edit source]

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):

Reader(); // the default constructor
~Reader();

// Read a value from a JSON document and store it to root.
// If collectComments is true, comments are stored, otherwise they are ignored.
// In case of syntax error, it returns false, and the value of root may be arbitrary.
bool parse(const std::string& document, Value& root, bool collectComments = true); // from std::string
bool parse(const char *beginDoc, const char *endDoc, Value &root, bool collectComments=true); // from C-style string
bool parse(std::istream &is, Value &root, bool collectComments=true); // from input stream

// Returns a user friendly string that list errors in the parsed document.
std::string getFormattedErrorMessages() const;

Example:

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Value val;
    Json::Reader reader;
    bool b = reader.parse(cin, val);
    if (!b)
        cout << "Error: " << reader.getFormattedErrorMessages();
    else
        cout << val;
}

Json::Writer

[edit | edit source]

Unlike Reader, the class Writer is abstract. There are two classes which implement it:

  • FastWriter produces unformatted, non-human-readable documents. Everything will be 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):

// omit the word "null" when printing null values
// this contradicts the JSON standard, but accepted by JavaScript
// this function is not available in old versions
void dropNullPlaceholders();

// don't add newline as last character
// this function is not available in old versions
void omitEndingLineFeed();
 
// print space after ":" in objects
void enableYAMLCompatibility();

// write JSON object to a string
virtual std::string write(const Value &root);

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

virtual std::string write(const Value &root); // write JSON object to a string

StyledStreamWriter

[edit | edit source]

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:

StyledStreamWriter(std::string indentation="\t");
~StyledStreamWriter();
void write(std::ostream &out, const Value &root);

StyledStreamWriter is not very useful, as operator<< is more convenient. You may want to use it if you want non-standard indentation.

Example:

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Value val;
    cin >> val;
    Json::FastWriter fast;
    Json::StyledWriter styled;
    string sFast = fast.write(val);
    string sStyled = styled.write(val);
    cout << "Fast:\n" << sFast << "Styled:\n" << sStyled;
    cout << "Styled stream:\n";
    Json::StyledStreamWriter styledStream;
    styledStream.write(cout, val);
}

Getting value and attributes

[edit | edit source]

Examining the attributes

[edit | edit source]

The following method of Value returns some info about it:

// get the type (intValue etc.)
ValueType type() const;

// get the number of elements in array or object (returns 0 for anything else, including string)
ArrayIndex size() const;

// returns true for empty array, empty object, or null
bool empty() const;

// returns true for null only
bool operator!() const;

// returns true for specific type only
bool isNull() const;
bool isBool() const;
bool isString() const;
bool isArray() const;
bool isObject() const;

// see explanations in text
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;

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 real.

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 versions 0.*, 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:

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Value val;
    cin >> val;
    switch (val.type()) {
        case Json::nullValue: cout << "nullValue\n"; break;
        case Json::intValue: cout << "intValue\n"; break;
        case Json::uintValue: cout << "uintValue\n"; break;
        case Json::realValue: cout << "realValue\n"; break;
        case Json::stringValue: cout << "stringValue\n"; break;
        case Json::booleanValue: cout << "booleanValue\n"; break;
        case Json::arrayValue: cout << "arrayValue\n"; break;
        case Json::objectValue: cout << "objectValue\n"; break;
        default: cout << "wrong type\n"; break;
    }
}

Getting the value

[edit | edit source]

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

  const char* asCString() const;
  std::string asString() const;
  Int asInt() const;
  UInt asUInt() const;
  Int64 asInt64() const;
  UInt64 asUInt64() const;
  LargestInt asLargestInt() const;
  LargestUInt asLargestUInt() const;
  float asFloat() const;
  double asDouble() const;
  bool asBool() const;

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 necessarily 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 std::string. It correctly treats strings with zero characters. It accepts everything except arrays and objects. For a null, the method returns ""; for a boolean, it returns "true" or "false"; for a number, it returns it's 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:

bool getString(char const** begin, char const** end) const;

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 :

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Value val;
    cin >> val;
    switch (val.type()) {
        case Json::nullValue: cout << "null\n"; break;
        case Json::intValue: cout << "int " << val.asLargestInt() << "\n"; break;
        case Json::uintValue: cout << "uint " << val.asLargestUInt() << "\n"; break;
        case Json::realValue: cout << "real " << val.asDouble() << "\n"; break;
        case Json::stringValue: cout << "string " << val.asString() << "\n"; break;
        /*
        -or-
        case Json::stringValue: {
            const char *begin;
            const char *end;
            val.getString(&begin, &end);
            cout << "string of length " << end - begin << "\n";
        }
        break;
        */
        case Json::booleanValue: cout << "boolean " << val.asBool() << "\n"; break;
        case Json::arrayValue: cout << "array of length " << val.size() << "\n"; break;
        case Json::objectValue: cout << "object of length " << val.size() << "\n"; break;
        default: cout << "wrong type\n"; break;
    }
}

Constructors, assignment and comparison

[edit | edit source]

The class Json::Value provides following constructors:

Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
Value(Int64 value);
Value(UInt64 value);
Value(double value);
Value(const char *value);
Value(const char *beginValue, const char *endValue);
Value(const std::string &value);
Value(bool value);
Value(const Value &other);

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

Assignment, swap, and all comparison operators are also provided (as methods).

Value &operator=(const Value &other);
void swap(Value &other);
bool operator<(const Value &other) const;
bool operator<=(const Value &other) const;
bool operator>=(const Value &other) const;
bool operator>(const Value &other) const;
bool operator==(const Value &other) const;
bool operator!=(const Value &other) const;
int compare(const Value &other) const;

Methods for arrays

[edit | edit source]

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

Some of them are similar to C++ STL's vectors:

ArrayIndex size() const;
bool empty() const;
void clear();
void resize(ArrayIndex size);
Value& operator[](ArrayIndex index);
Value& operator[](int index);
const Value& operator[](const ArrayIndex index) const;
const Value& operator[](const int index) const;

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:

Value &append(const Value &value);

This is similar to C++ method 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:

Value get(ArrayIndex index, const Value &defaultValue) const;

Note that it returns value NOT by reference, so calling this method may be very expensive.

To check validity of an index, you may want to use isValidIndex:

bool isValidIndex(ArrayIndex index) const;

This is not very useful, as value.isValidIndex(index) is equivalent to index < value.size().

You can also remove one value with removeIndex:

bool removeIndex(ArrayIndex i, Value* removed);

No miracles, this method takes linear time. If i is greater or equal to size, it returns false.

Calling array methods for null value

[edit | edit source]

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[i] (non-constant) transforms foo into an array of i+1 nulls, then returns the last value
  • foo[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

[edit | edit source]

The methods 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

[edit | edit source]

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

Some of them are similar to C++ STL's maps:

ArrayIndex size() const;
bool empty() const;
void clear();
Value& operator[](const char* key);
const Value& operator[](const char* key) const;
Value& operator[](const std::string& key);
const Value& operator[](const std::string& key) const;

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:

bool isMember(const char* key) const;
bool isMember(const std::string& key) const;
bool isMember(const char* begin, const char* end) const; // only in newer versions

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

Value removeMember(const char* key); // deprecated in versions 1.*
Value removeMember(const std::string& key); // deprecated in versions 1.*
bool removeMember(const char* key, Value* removed); // only since versions 1.*
bool removeMember(std::string const& key, Value* removed); // only since versions 1.*
bool removeMember(const char* begin, const char* end, Value* removed); // only since versions 1.*

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

Value::Members Value::getMemberNames() const;

Value::Members is defined as:

typedef std::vector<std::string> Members;

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.

Value get(const char* key, const Value& defaultValue) const;
Value get(const char* begin, const char* end, const Value& defaultValue) const;
Value get(const std::string& key, const Value& defaultValue) const; // only in newer versions

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.

const Value* find(const char* begin, const char* end) const; // only in newer versions

Calling object methods for null value

[edit | edit source]

The methods above treat null value as an empty object:

  • clear() does nothing
  • size() returns 0
  • empty() returns true
  • non-constant operator[] transforms the value into one-element object, with given key and null value
  • constant operator[] returns null
  • removeMember() returns null (deprecated variants) or false (new variants).
  • getMemberNames() returns empty vector of strings
  • get() returns defaultValue
  • find() returns NULL

Calling object methods for other types

[edit | edit source]

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

[edit | edit source]

Creating a complex structure:

#include <iostream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    // create the characters array
    Json::Value ch;
    ch[0]["name"] = "Jabberwock";
    ch[0]["chapter"] = 1;
    ch[1]["name"] = "Cheshire Cat";
    ch[1]["chapter"] = 6;
    ch[2]["name"] = "Mad Hatter";
    ch[2]["chapter"] = 7;

    // create the main object
    Json::Value val;
    val["book"] = "Alice in Wonderland";
    val["year"] = 1865;
    val["characters"] = ch;

    cout << val << '\n';
}

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

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

void Indent(ostream& ofs, int indent) {
    for (int i=0; i<indent; i++)
        ofs << ' ';
}

void MyPrint(ostream& ofs, const Json::Value& val, int indent=0) {
    switch (val.type()) {
        case Json::nullValue: ofs << "null"; break;
        case Json::booleanValue: ofs << (val.asBool() ? "true" : "false"); break;
        case Json::intValue: ofs << val.asLargestInt(); break;
        case Json::uintValue: ofs << val.asLargestUInt(); break;
        case Json::realValue: ofs << val.asDouble(); break;
        case Json::stringValue: ofs << '"' << val.asString() << '"'; break;
        case Json::arrayValue: {
            Json::ArrayIndex size = val.size();
            if (size == 0)
                ofs << "[]";
            else {
                ofs << "[\n";
                int newIndent = indent + 4;
                for (Json::ArrayIndex i=0; i<size; i++) {
                    Indent(ofs, newIndent);
                    MyPrint(ofs, val[i], newIndent);
                    ofs << (i + 1 == size ? "\n" : ",\n");
                }
                Indent(ofs, indent);
                ofs << ']';
            }
            break;
        }
        case Json::objectValue: {
            if (val.empty())
                ofs << "{}";
            else {
                ofs << "{\n";
                int newIndent = indent + 4;
                vector<string> keys = val.getMemberNames();
                for (size_t i=0; i<keys.size(); i++) {
                    Indent(ofs, newIndent);
                    const string& key = keys[i];
                    ofs << '"' << key << '"' << " : ";
                    MyPrint(ofs, val[key], newIndent);
                    ofs << (i + 1 == keys.size() ? "\n" : ",\n");
                }
                Indent(ofs, indent);
                ofs << '}';
            }
            break;
        }
        default :
            cerr << "Wrong type!" << endl;
            exit(0);
    }
}

int main() {
    ifstream ifs("alice.json");
    Json::Value val;
    ifs >> val;
    MyPrint(cout, val);
    cout << '\n';
}

Iterators

[edit | edit source]

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

const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();

Only arrays and objects have non-trivial iterators. If foo is neither array nor object, then foo.begin() equals to foo.end().

Iterators have the full set of operators: incrementing and decrementing (postfix and prefix ++ and --), comparison to equality and inequality, assignment, 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 foo is an iterator to an array, then *foo is a reference to the respective array's element. If foo is an iterator to an object, then *foo is NOT a reference to the (key, value) pair. It is a reference to the value itself.

operator-> is available since versions 1.* .

Example:

#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Reader reader;
    Json::Value val;
    reader.parse("{\"one\":1,\"two\":2,\"three\":3}",  val);
    for(Json::Value::const_iterator it=val.begin(); it!=val.end(); ++it)
        cout << it->asInt() << '\n';
}

will print:

1
3
2

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

// For iterator to array, returns the index
// For iterator to object, returs the key.
Value key() const;

// Return the index, or -1 if it is not an array iterator
UInt index() const;

// Return the key, or "" if it is not an object iterator.
const char *memberName() const;

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.

#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Reader reader;
    Json::Value val;
    reader.parse("{\"one\":1,\"two\":2,\"three\":3}",  val);
    for (Json::Value::const_iterator it=val.begin(); it!=val.end(); ++it)
        cout << it.key().asString() << ':' << it->asInt() << '\n';
}

This prints:

one:1
three:3
two:2

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

Iterators in old versions

[edit | edit source]

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

Json::Value::const_iterator it = val.begin();

Instead, we should use Json::Value::iterator. Besides, there is no operator-> .

Therefore, instead of the program above, we should write:

#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h> // or something

using namespace std;

int main() {
    Json::Reader reader;
    Json::Value val;
    reader.parse("{\"one\":1,\"two\":2,\"three\":3}",  val);
    // We cannot declare "it" as a const_iterator, because begin() and end()
    // return iterator, and there is no casting from iterator to const_iterator
    for (Json::Value::iterator it=val.begin(); it!=val.end(); ++it)
        cout << it.key().asString() << ':' << (*it).asInt() << '\n'; // no operator-> in this version
}