JsonCpp
A Wikibookian believes this page should be split into smaller pages with a narrower subtopic. You can help by splitting this big page into smaller ones. Please make sure to follow the naming policy. Dividing books into smaller sections can provide more focus and allow each one to do one thing well, which benefits everyone. |
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
[edit | edit source]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.
- Download and unzip the source from the official repository. Go to the directory.
- 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, otherwiselong long int
- UInt64 - a 64-bit unsigned integer, defined as
unsigned __int64
for Microsoft Visual Studio, otherwiseunsigned 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 stringasLargestInt()
for intasLargestUInt()
for uintasFloat()
orasDouble()
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 toasInt()
, 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
ordouble
. - 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[]
appendsindex-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()
returnstrue
foo.size()
returns 0foo.clear()
does nothingfoo.resize(0)
does nothingfoo.resize(size)
for positive size transformsfoo
into an array ofsize
nulls.foo[i]
(non-constant) transformsfoo
into an array ofi+1
nulls, then returns the last valuefoo[i]
(constant) returns the null valuefoo.isValidIndex(i)
always returnsfalse
foo.get(index, defaultValue)
always returnsdefaultValue
foo.append(bar)
makesfoo
an array of one element, which equals tobar
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()
returnsfalse
, unlessfoo
is an empty objectfoo.size()
returns 0, unlessfoo
is an objectfoo.clear()
throws thestd::runtime_error
exception, unlessfoo
is an objectresize
,append
,get
,operator[]
throw thestd::runtime_error
exceptionisValidIndex
always returnsfalse
removeIndex
always returnsfalse
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 nothingsize()
returns 0empty()
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) orfalse
(new variants).getMemberNames()
returns empty vector of stringsget()
returnsdefaultValue
find()
returnsNULL
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
}