Commit 4502e7e5 by Niels

- initial commit

parents
noinst_PROGRAMS = json json98
TESTS = ./json ./json98
#CXXFLAGS = -O3 -flto
json_SOURCES = src/JSON.cc src/JSON.h test/JSON_test.cc
json_CXXFLAGS = -std=c++11
json_CPPFLAGS = -I$(top_srcdir)/src
json98_SOURCES = src/JSON.cc src/JSON.h test/JSON_test.cc
json98_CXXFLAGS = -std=c++98
json98_CPPFLAGS = -I$(top_srcdir)/src
svn-clean: maintainer-clean
rm -fr configure INSTALL aclocal.m4 build-aux depcomp install-sh missing test-driver
for DIR in $(DIST_SUBDIRS) .; do rm -f $$DIR/Makefile.in; done
AC_PREREQ([2.69])
AC_INIT([JSON], [1.0], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([src/JSON.cc])
AM_INIT_AUTOMAKE([foreign])
AM_SILENT_RULES([yes])
AC_PROG_CXX
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
#include "JSON.h"
#include <utility>
#include <stdexcept>
#include <fstream>
#include <cctype>
#include <iostream>
#include <streambuf>
#include <sstream>
#ifndef __cplusplus11
#include <cstring>
#include <cstdlib>
#endif
#ifdef __cplusplus11
using std::to_string;
#else
inline std::string to_string(double f) {
std::stringstream s;
s << f;
return s.str();
}
#endif
/******************
* STATIC MEMBERS *
******************/
#ifdef __cplusplus11
/// a mutex to ensure thread safety
std::mutex JSON::_token;
#endif
/*******************************
* CONSTRUCTORS AND DESTRUCTOR *
*******************************/
JSON::JSON() : _type(null), _payload(nullptr) {}
JSON::JSON(const std::string& s) : _type(string), _payload(new std::string(s)) {}
JSON::JSON(const char* s) : _type(string), _payload(new std::string(s)) {}
JSON::JSON(char* s) : _type(string), _payload(new std::string(s)) {}
JSON::JSON(const bool b) : _type(boolean), _payload(new bool(b)) {}
JSON::JSON(const int i) : _type(number_int), _payload(new int(i)) {}
JSON::JSON(const double f) : _type(number_float), _payload(new double(f)) {}
#ifdef __cplusplus11
JSON::JSON(array_t a) : _type(array), _payload(new std::vector<JSON>(a)) {}
JSON::JSON(object_t o) : _type(object), _payload(new std::map<std::string, JSON>) {
(*this)[std::get<0>(o)] = std::get<1>(o);
}
#endif
/// copy constructor
JSON::JSON(const JSON& o) : _type(o._type) {
switch (_type) {
case (array):
_payload = new std::vector<JSON>(*static_cast<std::vector<JSON>*>(o._payload));
break;
case (object):
_payload = new std::map<std::string, JSON>(*static_cast<std::map<std::string, JSON>*>(o._payload));
break;
case (string):
_payload = new std::string(*static_cast<std::string*>(o._payload));
break;
case (boolean):
_payload = new bool(*static_cast<bool*>(o._payload));
break;
case (number_int):
_payload = new int(*static_cast<int*>(o._payload));
break;
case (number_float):
_payload = new double(*static_cast<double*>(o._payload));
break;
case (null):
break;
}
}
#ifdef __cplusplus11
/// move constructor
JSON::JSON(JSON&& o) : _type(std::move(o._type)), _payload(std::move(o._payload)) {}
#endif
/// copy assignment
#ifdef __cplusplus11
JSON& JSON::operator=(JSON o) {
std::swap(_type, o._type);
std::swap(_payload, o._payload);
return *this;
}
#else
JSON& JSON::operator=(const JSON& o) {
_type = o._type;
switch (_type) {
case (array):
_payload = new std::vector<JSON>(*static_cast<std::vector<JSON>*>(o._payload));
break;
case (object):
_payload = new std::map<std::string, JSON>(*static_cast<std::map<std::string, JSON>*>(o._payload));
break;
case (string):
_payload = new std::string(*static_cast<std::string*>(o._payload));
break;
case (boolean):
_payload = new bool(*static_cast<bool*>(o._payload));
break;
case (number_int):
_payload = new int(*static_cast<int*>(o._payload));
break;
case (number_float):
_payload = new double(*static_cast<double*>(o._payload));
break;
case (null):
break;
}
return *this;
}
#endif
/// destructor
JSON::~JSON() {
switch (_type) {
case (array):
delete static_cast<std::vector<JSON>*>(_payload);
break;
case (object):
delete static_cast<std::map<std::string, JSON>*>(_payload);
break;
case (string):
delete static_cast<std::string*>(_payload);
break;
case (boolean):
delete static_cast<bool*>(_payload);
break;
case (number_int):
delete static_cast<int*>(_payload);
break;
case (number_float):
delete static_cast<double*>(_payload);
break;
case (null):
break;
}
}
/*****************************
* OPERATORS AND CONVERSIONS *
*****************************/
JSON::operator const std::string() const {
switch (_type) {
case (string):
return *static_cast<std::string*>(_payload);
default:
throw std::runtime_error("cannot cast " + _typename() + " to JSON string");
}
}
JSON::operator int() const {
switch (_type) {
case (number_int):
return *static_cast<int*>(_payload);
case (number_float):
return static_cast<int>(*static_cast<double*>(_payload));
default:
throw std::runtime_error("cannot cast " + _typename() + " to JSON number");
}
}
JSON::operator double() const {
switch (_type) {
case (number_int):
return static_cast<double>(*static_cast<int*>(_payload));
case (number_float):
return *static_cast<double*>(_payload);
default:
throw std::runtime_error("cannot cast " + _typename() + " to JSON number");
}
}
JSON::operator bool() const {
switch (_type) {
case (boolean):
return *static_cast<bool*>(_payload);
default:
throw std::runtime_error("cannot cast " + _typename() + " to JSON Boolean");
}
}
const std::string JSON::toString() const {
switch (_type) {
case (null): {
return "null";
}
case (string): {
return std::string("\"") + *static_cast<std::string*>(_payload) + "\"";
}
case (boolean): {
return *static_cast<bool*>(_payload) ? "true" : "false";
}
case (number_int): {
return to_string(*static_cast<int*>(_payload));
}
case (number_float): {
return to_string(*static_cast<double*>(_payload));
}
case (array): {
std::string result;
const std::vector<JSON>* array = static_cast<std::vector<JSON>*>(_payload);
for (std::vector<JSON>::const_iterator i = array->begin(); i != array->end(); ++i) {
if (i != array->begin()) {
result += ", ";
}
result += (*i).toString();
}
return "[" + result + "]";
}
case (object): {
std::string result;
const std::map<std::string, JSON>* object = static_cast<std::map<std::string, JSON>*>(_payload);
for (std::map<std::string, JSON>::const_iterator i = object->begin(); i != object->end(); ++i) {
if (i != object->begin()) {
result += ", ";
}
result += "\"" + i->first + "\": " + (i->second).toString();
}
return "{" + result + "}";
}
}
}
/*****************************************
* ADDING ELEMENTS TO OBJECTS AND ARRAYS *
*****************************************/
JSON& JSON::operator+=(const JSON& o) {
#ifdef __cplusplus11
std::lock_guard<std::mutex> lg(_token);
#endif
if (not(_type == null or _type == array)) {
throw std::runtime_error("cannot add element to " + _typename());
}
if (_type == null) {
_type = array;
_payload = new std::vector<JSON>;
}
static_cast<std::vector<JSON>*>(_payload)->push_back(o);
return *this;
}
JSON& JSON::operator+=(const std::string& s) {
JSON tmp(s);
operator+=(tmp);
return *this;
}
JSON& JSON::operator+=(const char* s) {
JSON tmp(s);
operator+=(tmp);
return *this;
}
JSON& JSON::operator+=(bool b) {
JSON tmp(b);
operator+=(tmp);
return *this;
}
JSON& JSON::operator+=(int i) {
JSON tmp(i);
operator+=(tmp);
return *this;
}
JSON& JSON::operator+=(double f) {
JSON tmp(f);
operator+=(tmp);
return *this;
}
/// operator to set an element in an object
JSON& JSON::operator[](int index) {
#ifdef __cplusplus11
std::lock_guard<std::mutex> lg(_token);
#endif
if (_type != array) {
throw std::runtime_error("cannot add entry with index " + to_string(index) + " to " + _typename());
}
std::vector<JSON>* array = static_cast<std::vector<JSON>*>(_payload);
if (index >= array->size()) {
throw std::runtime_error("cannot access element at index " + to_string(index));
}
return array->at(index);
}
/// operator to get an element in an object
const JSON& JSON::operator[](const int index) const {
if (_type != array) {
throw std::runtime_error("cannot get entry with index " + to_string(index) + " from " + _typename());
}
std::vector<JSON>* array = static_cast<std::vector<JSON>*>(_payload);
if (index >= array->size()) {
throw std::runtime_error("cannot access element at index " + to_string(index));
}
return array->at(index);
}
/// operator to set an element in an object
JSON& JSON::operator[](const std::string& key) {
#ifdef __cplusplus11
std::lock_guard<std::mutex> lg(_token);
#endif
if (_type == null) {
_type = object;
_payload = new std::map<std::string, JSON>;
}
if (_type != object) {
throw std::runtime_error("cannot add entry with key " + std::string(key) + " to " + _typename());
}
std::map<std::string, JSON>* object = static_cast<std::map<std::string, JSON>*>(_payload);
if (object->find(key) == object->end()) {
(*object)[key] = JSON();
}
return (*object)[key];
}
/// operator to set an element in an object
JSON& JSON::operator[](const char* key) {
#ifdef __cplusplus11
std::lock_guard<std::mutex> lg(_token);
#endif
if (_type == null) {
_type = object;
_payload = new std::map<std::string, JSON>;
}
if (_type != object) {
throw std::runtime_error("cannot add entry with key " + std::string(key) + " to " + _typename());
}
std::map<std::string, JSON>* object = static_cast<std::map<std::string, JSON>*>(_payload);
if (object->find(key) == object->end()) {
(*object)[key] = JSON();
}
return (*object)[key];
}
/// operator to get an element in an object
const JSON& JSON::operator[](const std::string& key) const {
if (_type != object) {
throw std::runtime_error("cannot get entry with key " + std::string(key) + " from " + _typename());
}
const std::map<std::string, JSON>* object = static_cast<std::map<std::string, JSON>*>(_payload);
if (object->find(key) == object->end()) {
throw std::runtime_error("key " + key + " not found");
} else {
return object->find(key)->second;
}
}
/// return the number of stored values
size_t JSON::size() const {
switch (_type) {
case (array):
return static_cast<std::vector<JSON>*>(_payload)->size();
case (object):
return static_cast<std::map<std::string, JSON>*>(_payload)->size();
case (null):
return 0;
case (string):
return 1;
case (boolean):
return 1;
case (number_int):
return 1;
case (number_float):
return 1;
}
}
/// checks whether object is empty
bool JSON::empty() const {
switch (_type) {
case (array):
return static_cast<std::vector<JSON>*>(_payload)->empty();
case (object):
return static_cast<std::map<std::string, JSON>*>(_payload)->empty();
case (null):
return true;
case (string):
return false;
case (boolean):
return false;
case (number_int):
return false;
case (number_float):
return false;
}
}
/// return the type of the object
JSON::json_t JSON::type() const {
return _type;
}
/// direct access to the underlying payload
void* JSON::data() {
return _payload;
}
/// direct access to the underlying payload
const void* JSON::data() const {
return _payload;
}
/// lexicographically compares the values
bool JSON::operator==(const JSON& o) const {
switch (_type) {
case (array): {
if (o._type == array) {
std::vector<JSON>* a = static_cast<std::vector<JSON>*>(_payload);
std::vector<JSON>* b = static_cast<std::vector<JSON>*>(o._payload);
return *a == *b;
}
}
case (object): {
if (o._type == object) {
std::map<std::string, JSON>* a = static_cast<std::map<std::string, JSON>*>(_payload);
std::map<std::string, JSON>* b = static_cast<std::map<std::string, JSON>*>(o._payload);
return *a == *b;
}
}
case (null): {
if (o._type == null) {
return true;
}
}
case (string): {
if (o._type == string) {
const std::string a = *this;
const std::string b = o;
return a == b;
}
}
case (boolean): {
if (o._type == boolean) {
bool a = *this;
bool b = o;
return a == b;
}
}
case (number_int): {
if (o._type == number_int or o._type == number_float) {
int a = *this;
int b = o;
return a == b;
}
}
case (number_float): {
if (o._type == number_int or o._type == number_float) {
double a = *this;
double b = o;
return a == b;
}
}
}
return false;
}
/// return the type as string
std::string JSON::_typename() const {
switch (_type) {
case (array):
return "array";
case (object):
return "object";
case (null):
return "null";
case (string):
return "string";
case (boolean):
return "boolean";
case (number_int):
return "number";
case (number_float):
return "number";
}
}
JSON::parser::parser(char* s) : _pos(0) {
_buffer = new char[strlen(s) + 1];
strcpy(_buffer, s);
// read first character
next();
}
JSON::parser::parser(std::string& s) : _pos(0) {
_buffer = new char[s.length() + 1];
strcpy(_buffer, s.c_str());
// read first character
next();
}
JSON::parser::parser(std::istream& _is) : _pos(0) {
// determine length of input stream
_is.seekg(0, std::ios::end);
std::streampos length = _is.tellg();
_is.seekg(0, std::ios::beg);
// copy stream to buffer
_buffer = new char[length];
_is.read(_buffer, length);
// read first character
next();
}
JSON::parser::~parser() {
delete [] _buffer;
}
void JSON::parser::error(std::string msg) {
throw std::runtime_error("parse error at position " + to_string(_pos) + ": " + msg + ", last read: '" + _current + "'");
}
bool JSON::parser::next() {
_current = _buffer[_pos++];
if (_buffer == nullptr) {
return false;
}
// skip trailing whitespace
while (std::isspace(_current)) {
_current = _buffer[_pos++];
}
return true;
}
/// \todo: escaped strings
std::string JSON::parser::parseString() {
// get position of closing quote
const char* p = strchr(_buffer + _pos, '\"');
// check if quotes were found
if (!p) {
error("expected '\"'");
}
// copy string to return value
const size_t length = p - _buffer - _pos;
char* tmp = new char[length + 1];
strncpy(tmp, _buffer + _pos, length);
std::string result(tmp);
delete [] tmp;
// +1 to eat closing quote
_pos += length + 1;
// read next character
next();
return result;
}
void JSON::parser::parseTrue() {
if (strncmp(_buffer + _pos, "rue", 3)) {
error("expected true");
}
_pos += 3;
// read next character
next();
}
void JSON::parser::parseFalse() {
if (strncmp(_buffer + _pos, "alse", 4)) {
error("expected false");
}
_pos += 4;
// read next character
next();
}
void JSON::parser::parseNull() {
if (strncmp(_buffer + _pos, "ull", 3)) {
error("expected null");
}
_pos += 3;
// read next character (optional?)
next();
}
void JSON::parser::expect(char c) {
if (_current != c) {
std::string msg = "expected '";
msg.append(1, c);
msg += "'";
error(msg.c_str());
} else {
next();
}
}
void JSON::parser::parse(JSON& result) {
if (!_buffer) {
error("unexpected end of file");
}
//JSON result;
switch (_current) {
case ('{'): {
// explicitly set result to object to cope with {}
result._type = object;
result._payload = new std::map<std::string, JSON>;
next();
// process nonempty object
if (_current != '}') {
do {
// key
const std::string key = parseString();
// colon
expect(':');
// value
parse(result[key]);
} while (_current == ',' && next());
}
// closing brace
expect('}');
break;
}
case ('['): {
// explicitly set result to array to cope with []
result._type = array;
result._payload = new std::vector<JSON>;
next();
// process nonempty array
if (_current != ']') {
size_t element_count = 0;
do {
// add a dummy value and continue parsing at its position
result += JSON();
parse(result[element_count++]);
} while (_current == ',' && next());
}
// closing bracket
expect(']');
break;
}
case ('\"'): {
result._type = string;
result._payload = new std::string(parseString());
break;
}
case ('t'): {
parseTrue();
result._type = boolean;
result._payload = new bool(true);
break;
}
case ('f'): {
parseFalse();
result._type = boolean;
result._payload = new bool(false);
break;
}
case ('n'): {
parseNull();
// nothing to do with result: is null by default
break;
}
default: {
if (std::isdigit(_current) || _current == '-') {
// collect number in tmp string
std::string tmp;
do {
tmp += _current;
next();
} while (std::isdigit(_current) || _current == '.' || _current == 'e' || _current == 'E' || _current == '+' || _current == '-');
if (tmp.find(".") == std::string::npos) {
// integer (we use atof, because it can cope with e)
result._type = number_int;
result._payload = new int(std::atof(tmp.c_str()));
} else {
// float
result._type = number_float;
result._payload = new double(std::atof(tmp.c_str()));
}
break;
} else {
error("unexpected character");
}
}
}
}
// http://stackoverflow.com/questions/7758580/writing-your-own-stl-container/7759622#7759622
JSON::iterator JSON::begin() {
return JSON::iterator(this);
}
JSON::iterator JSON::end() {
return JSON::iterator();
}
JSON::iterator::iterator() : _object(nullptr), _vi(nullptr), _oi(nullptr) {}
JSON::iterator::iterator(JSON* j) : _object(j), _vi(nullptr), _oi(nullptr) {
switch (_object->_type) {
case (array): {
_vi = new std::vector<JSON>::iterator(static_cast<std::vector<JSON>*>(_object->_payload)->begin());
break;
}
case (object): {
_oi = new std::map<std::string, JSON>::iterator(static_cast<std::map<std::string, JSON>*>(_object->_payload)->begin());
break;
}
default:
break;
}
}
JSON::iterator::iterator(const JSON::iterator& o) : _object(o._object), _vi(nullptr), _oi(nullptr) {
switch (_object->_type) {
case (array): {
_vi = new std::vector<JSON>::iterator(static_cast<std::vector<JSON>*>(_object->_payload)->begin());
break;
}
case (object): {
_oi = new std::map<std::string, JSON>::iterator(static_cast<std::map<std::string, JSON>*>(_object->_payload)->begin());
break;
}
default:
break;
}
}
JSON::iterator::~iterator() {
delete _vi;
delete _oi;
}
JSON::iterator& JSON::iterator::operator=(const JSON::iterator& o) {
_object = o._object;
return *this;
}
bool JSON::iterator::operator==(const JSON::iterator& o) const {
return _object == o._object;
}
bool JSON::iterator::operator!=(const JSON::iterator& o) const {
return _object != o._object;
}
JSON::iterator& JSON::iterator::operator++() {
// iterator cannot be incremented
if (_object == nullptr) {
return *this;
}
switch (_object->_type) {
case (array): {
if (++(*_vi) == static_cast<std::vector<JSON>*>(_object->_payload)->end()) {
_object = nullptr;
}
break;
}
case (object): {
if (++(*_oi) == static_cast<std::map<std::string, JSON>*>(_object->_payload)->end()) {
_object = nullptr;
}
break;
}
default: {
_object = nullptr;
}
}
return *this;
}
JSON& JSON::iterator::operator*() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return **_vi;
case (object):
return (*_oi)->second;
default:
return *_object;
}
}
JSON* JSON::iterator::operator->() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return &(**_vi);
case (object):
return &((*_oi)->second);
default:
return _object;
}
}
std::string JSON::iterator::key() const {
if (_object != nullptr and _object->_type == object) {
return (*_oi)->first;
} else {
throw std::runtime_error("cannot get key");
}
}
JSON& JSON::iterator::value() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return **_vi;
case (object):
return (*_oi)->second;
default:
return *_object;
}
}
JSON::const_iterator JSON::begin() const {
return JSON::const_iterator(this);
}
JSON::const_iterator JSON::end() const {
return JSON::const_iterator();
}
JSON::const_iterator JSON::cbegin() const {
return JSON::const_iterator(this);
}
JSON::const_iterator JSON::cend() const {
return JSON::const_iterator();
}
JSON::const_iterator::const_iterator() : _object(nullptr), _vi(nullptr), _oi(nullptr) {}
JSON::const_iterator::const_iterator(const JSON* j) : _object(j), _vi(nullptr), _oi(nullptr) {
switch (_object->_type) {
case (array): {
_vi = new std::vector<JSON>::const_iterator(static_cast<std::vector<JSON>*>(_object->_payload)->begin());
break;
}
case (object): {
_oi = new std::map<std::string, JSON>::const_iterator(static_cast<std::map<std::string, JSON>*>(_object->_payload)->begin());
break;
}
default:
break;
}
}
JSON::const_iterator::const_iterator(const JSON::const_iterator& o) : _object(o._object), _vi(nullptr), _oi(nullptr) {
switch (_object->_type) {
case (array): {
_vi = new std::vector<JSON>::const_iterator(static_cast<std::vector<JSON>*>(_object->_payload)->begin());
break;
}
case (object): {
_oi = new std::map<std::string, JSON>::const_iterator(static_cast<std::map<std::string, JSON>*>(_object->_payload)->begin());
break;
}
default:
break;
}
}
JSON::const_iterator::const_iterator(const JSON::iterator& o) : _object(o._object), _vi(nullptr), _oi(nullptr) {
switch (_object->_type) {
case (array): {
_vi = new std::vector<JSON>::const_iterator(static_cast<std::vector<JSON>*>(_object->_payload)->begin());
break;
}
case (object): {
_oi = new std::map<std::string, JSON>::const_iterator(static_cast<std::map<std::string, JSON>*>(_object->_payload)->begin());
break;
}
default:
break;
}
}
JSON::const_iterator::~const_iterator() {
delete _vi;
delete _oi;
}
JSON::const_iterator& JSON::const_iterator::operator=(const JSON::const_iterator& o) {
_object = o._object;
return *this;
}
bool JSON::const_iterator::operator==(const JSON::const_iterator& o) const {
return _object == o._object;
}
bool JSON::const_iterator::operator!=(const JSON::const_iterator& o) const {
return _object != o._object;
}
JSON::const_iterator& JSON::const_iterator::operator++() {
// iterator cannot be incremented
if (_object == nullptr) {
return *this;
}
switch (_object->_type) {
case (array): {
if (++(*_vi) == static_cast<std::vector<JSON>*>(_object->_payload)->end()) {
_object = nullptr;
}
break;
}
case (object): {
if (++(*_oi) == static_cast<std::map<std::string, JSON>*>(_object->_payload)->end()) {
_object = nullptr;
}
break;
}
default: {
_object = nullptr;
}
}
return *this;
}
const JSON& JSON::const_iterator::operator*() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return **_vi;
case (object):
return (*_oi)->second;
default:
return *_object;
}
}
const JSON* JSON::const_iterator::operator->() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return &(**_vi);
case (object):
return &((*_oi)->second);
default:
return _object;
}
}
std::string JSON::const_iterator::key() const {
if (_object != nullptr and _object->_type == object) {
return (*_oi)->first;
} else {
throw std::runtime_error("cannot get key");
}
}
const JSON& JSON::const_iterator::value() const {
if (_object == nullptr) {
throw std::runtime_error("cannot get value");
}
switch (_object->_type) {
case (array):
return **_vi;
case (object):
return (*_oi)->second;
default:
return *_object;
}
}
#pragma once
// a helper macro to detect C++11 compliant compilers
#if __cplusplus >= 201103L
#define __cplusplus11
#endif
// allow us to use "nullptr" everywhere
#ifndef nullptr
#define nullptr NULL
#endif
#include <string>
#include <vector>
#include <map>
// additional C++11 header
#ifdef __cplusplus11
#include <mutex>
#include <initializer_list>
#endif
class JSON {
private:
#ifdef __cplusplus11
/// mutex to guard payload
static std::mutex _token;
#endif
public:
/// possible types of a JSON object
typedef enum {
array, object, null, string, boolean, number_int, number_float
} json_t;
private:
/// the type of this object
json_t _type;
/// the payload
void* _payload;
public:
#ifdef __cplusplus11
/// a type for objects
typedef std::tuple<std::string, JSON> object_t;
/// a type for arrays
typedef std::initializer_list<JSON> array_t;
#endif
public:
/// create an empty (null) object
JSON();
/// create a string object from C++ string
JSON(const std::string&);
/// create a string object from C string
JSON(char*);
/// create a string object from C string
JSON(const char*);
/// create a Boolean object
JSON(const bool);
/// create a number object
JSON(const int);
/// create a number object
JSON(const double);
#ifdef __cplusplus11
/// create from an initializer list (to an array)
JSON(array_t);
/// create from a mapping (to an object)
JSON(object_t);
#endif
/// copy constructor
JSON(const JSON&);
#ifdef __cplusplus11
/// move constructor
JSON(JSON&&);
#endif
/// copy assignment
#ifdef __cplusplus11
JSON& operator=(JSON);
#else
JSON& operator=(const JSON&);
#endif
/// destructor
~JSON();
/// implicit conversion to string representation
operator const std::string() const;
/// implicit conversion to integer (only for numbers)
operator int() const;
/// implicit conversion to double (only for numbers)
operator double() const;
/// implicit conversion to Boolean (only for Booleans)
operator bool() const;
/// write to stream
friend std::ostream& operator<<(std::ostream& o, const JSON& j) {
o << j.toString();
return o;
}
/// write to stream
friend std::ostream& operator>>(const JSON& j, std::ostream& o) {
o << j.toString();
return o;
}
/// read from stream
friend std::istream& operator>>(std::istream& i, JSON& j) {
parser(i).parse(j);
return i;
}
/// read from stream
friend std::istream& operator<<(JSON& j, std::istream& i) {
parser(i).parse(j);
return i;
}
/// explicit conversion to string representation (C++ style)
const std::string toString() const;
/// add an object/array to an array
JSON& operator+=(const JSON&);
/// add a string to an array
JSON& operator+=(const std::string&);
/// add a string to an array
JSON& operator+=(const char*);
/// add a Boolean to an array
JSON& operator+=(bool);
/// add a number to an array
JSON& operator+=(int);
/// add a number to an array
JSON& operator+=(double);
/// operator to set an element in an array
JSON& operator[](int);
/// operator to get an element in an array
const JSON& operator[](const int) const;
/// operator to set an element in an object
JSON& operator[](const std::string&);
/// operator to set an element in an object
JSON& operator[](const char*);
/// operator to get an element in an object
const JSON& operator[](const std::string&) const;
/// return the number of stored values
size_t size() const;
/// checks whether object is empty
bool empty() const;
/// return the type of the object
json_t type() const;
/// direct access to the underlying payload
void* data();
/// direct access to the underlying payload
const void* data() const;
/// lexicographically compares the values
bool operator==(const JSON&) const;
private:
/// return the type as string
std::string _typename() const;
// forward declaration to friend this class
public:
class const_iterator;
public:
/// an iterator
class iterator {
friend class JSON::const_iterator;
public:
iterator();
iterator(JSON*);
iterator(const iterator&);
~iterator();
iterator& operator=(const iterator&);
bool operator==(const iterator&) const;
bool operator!=(const iterator&) const;
iterator& operator++();
JSON& operator*() const;
JSON* operator->() const;
/// getter for the key (in case of objects)
std::string key() const;
/// getter for the value
JSON& value() const;
private:
/// a JSON value
JSON* _object;
/// an iterator for JSON arrays
std::vector<JSON>::iterator* _vi;
/// an iterator for JSON objects
std::map<std::string, JSON>::iterator* _oi;
};
/// a const iterator
class const_iterator {
public:
const_iterator();
const_iterator(const JSON*);
const_iterator(const const_iterator&);
const_iterator(const iterator&);
~const_iterator();
const_iterator& operator=(const const_iterator&);
bool operator==(const const_iterator&) const;
bool operator!=(const const_iterator&) const;
const_iterator& operator++();
const JSON& operator*() const;
const JSON* operator->() const;
/// getter for the key (in case of objects)
std::string key() const;
/// getter for the value
const JSON& value() const;
private:
/// a JSON value
const JSON* _object;
/// an iterator for JSON arrays
std::vector<JSON>::const_iterator* _vi;
/// an iterator for JSON objects
std::map<std::string, JSON>::const_iterator* _oi;
};
public:
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
private:
class parser {
public:
parser(char*);
parser(std::string&);
parser(std::istream&);
~parser();
void parse(JSON&);
private:
bool next();
void error(std::string = "");
std::string parseString();
void parseTrue();
void parseFalse();
void parseNull();
void expect(char);
char _current;
char* _buffer;
size_t _pos;
};
};
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cassert>
#include <JSON.h>
#include <sstream>
void test_null() {
/* a null object */
// construct
JSON a, b;
// copy assign
b = JSON();
// copy construct
JSON c(a);
// copy construct
JSON d = a;
// assign operator
JSON e = JSON();
// compare
assert(a == b);
// type
assert(a.type() == JSON::null);
// empty and size
assert(a.size() == 0);
assert(a.empty() == true);
// output
std::cout << a << '\n';
// string represetations
assert(a.toString() == std::string("null"));
// invalid conversion to int
try {
int i = 0;
i = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast null to JSON number"));
}
// invalid conversion to double
try {
double f = 0;
f = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast null to JSON number"));
}
// invalid conversion to bool
try {
bool b = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast null to JSON Boolean"));
}
// invalid conversion to string
try {
std::string s = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast null to JSON string"));
}
}
void test_string() {
/* a string object */
// construct
JSON a = "object a";
JSON b;
// copy assign
b = JSON("object a");
// copy construct
JSON c(a);
// copy construct
JSON d = a;
// assign operator
JSON e = JSON("");
// compare
assert(a == b);
// type
assert(a.type() == JSON::string);
// empty and size
assert(a.size() == 1);
assert(a.empty() == false);
// output
std::cout << a << '\n';
// string represetations
assert(a.toString() == std::string("\"object a\""));
// invalid conversion to int
try {
int i = 0;
i = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast string to JSON number"));
}
// invalid conversion to double
try {
double f = 0;
f = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast string to JSON number"));
}
// invalid conversion to bool
try {
bool b = false;
b = a;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot cast string to JSON Boolean"));
}
}
void test_array() {
JSON a;
a += JSON();
a += 1;
a += 1.0;
a += true;
a += "string";
// type
assert(a.type() == JSON::array);
// empty and size
assert(a.size() == 5);
assert(a.empty() == false);
// output
std::cout << a << '\n';
// check for elements
assert(a[1] == JSON(1));
assert(a[2] == JSON(1.0));
assert(a[3] == JSON(true));
assert(a[4] == JSON("string"));
// invalid access to element
try {
a[5] = 1;
assert(false);
} catch (const std::exception& ex) {
assert(ex.what() == std::string("cannot access element at index 5"));
}
// get elements
{
int i = a[1];
double d = a[2];
bool b = a[3];
std::string s = a[4];
}
// set elements
a[1] = 2;
#ifdef __cplusplus11
// construction from initializer list
JSON b = {JSON(), 2, 1.0, true, "string"};
assert(a == b);
#endif
// iterators
for (JSON::iterator i = a.begin(); i != a.end(); ++i) {
std::cerr << *i << '\n';
}
for (JSON::const_iterator i = a.cbegin(); i != a.cend(); ++i) {
std::cerr << *i << '\n';
}
#ifdef __cplusplus11
for (auto element : a) {
std::cerr << element << '\n';
}
#endif
}
void test_streaming() {
// stream text representation into stream
std::stringstream i;
i << "{ \"foo\": true, \"baz\": [1,2,3,4] }";
// create JSON from stream
{
JSON j, k;
i >> j;
k << i;
assert(j.toString() == k.toString());
}
// roundtrip
{
std::stringstream o;
JSON j, k;
i >> j;
j >> o;
o >> k;
assert(j.toString() == k.toString());
}
}
int main() {
test_null();
test_string();
test_array();
test_streaming();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment