Skip to content

Instantly share code, notes, and snippets.

@mosra
Created November 21, 2011 22:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mosra/1384168 to your computer and use it in GitHub Desktop.
Save mosra/1384168 to your computer and use it in GitHub Desktop.
Bourne, a JSON parser library
#include "Bourne.h"
#define forever for(;;)
using namespace std;
namespace Bourne {
void AbstractValue::ignorespace(istream& in) {
while(isspace(in.peek()))
in.ignore();
}
AbstractValue* AbstractValue::deserialize(istream& in) {
char c = in.peek();
switch(c) {
case '{': return new Object(in);
case '[': return new Array(in);
case '"': return new String(in);
case 'f':
case 't': return new Boolean(in);
case 'n':
in.ignore(4);
return Null;
default: return new Integer(in);
}
}
void AbstractValue::serialize(ostream& out, AbstractValue* value, size_t indent) {
if(!value) out << "null";
else value->serialize(out, indent);
}
Object::Object(istream& in) {
ignorespace(in);
/* Ignore opening bracket */
in.ignore();
ignorespace(in);
forever {
/* Parse key string */
String key(in);
ignorespace(in);
/* Ignore colon */
in.ignore();
ignorespace(in);
/* Parse value */
AbstractValue* value = AbstractValue::deserialize(in);
add(key, value);
ignorespace(in);
/* If there is comma, continue, done otherwise */
if(in.get() == '}')
return;
ignorespace(in);
}
}
Object::~Object() {
for(iterator it = begin(); it != end(); ++it)
delete it->second;
}
AbstractValue* Object::at(const string& key) const {
map<string, AbstractValue*>::const_iterator found = m.find(key);
if(found == m.end()) return 0;
return found->second;
}
bool Object::remove(const string& key) {
map<string, AbstractValue*>::iterator found = m.find(key);
if(found == m.end()) return false;
delete found->second;
m.erase(found);
return true;
}
void Object::serialize(ostream& out, size_t indent) const {
/* Opening bracket */
out << '{' << endl;
/* Increase indentation */
indent += IndentWidth;
string s(indent, ' ');
/* Serialize all elements */
for(const_iterator it = begin(); it != end(); ++it) {
if(it != begin()) out << ',' << endl;
out << s;
String(it->first).serialize(out, indent);
out << ": ";
AbstractValue::serialize(out, it->second, indent);
}
if(!m.empty()) out << endl;
/* Closing bracket */
out << string(indent - IndentWidth, ' ') << '}';
}
Array::Array(istream& in) {
/* Ignore opening bracket */
in.ignore();
ignorespace(in);
forever {
/* Parse value */
AbstractValue* value = AbstractValue::deserialize(in);
add(value);
ignorespace(in);
/* If there is comma, continue, done otherwise */
if(in.get() == ']')
return;
ignorespace(in);
}
}
Array::~Array() {
for(iterator it = begin(); it != end(); ++it)
delete *it;
}
void Array::remove(size_t index) {
vector<AbstractValue*>::iterator it = a.begin()+index;
delete *it;
a.erase(it);
}
void Array::serialize(ostream& out, size_t indent) const {
/* Opening bracket */
out << '[' << endl;
/* Increase indentation */
indent += IndentWidth;
string s(indent, ' ');
/* Serialize all elements */
for(const_iterator it = begin(); it != end(); ++it) {
out << s;
AbstractValue::serialize(out, *it, indent);
if(it != end()-1) out << ',';
out << endl;
}
/* Closing bracket */
out << string(indent - IndentWidth, ' ') << ']';
}
String::String(istream& in) {
/* Ignore opening quote */
in.ignore();
bool escaped = false;
while(in.good()) {
char c = in.get();
switch(c) {
case '\\':
if(escaped) v.push_back('\\');
escaped = !escaped;
break;
case '"':
if(!escaped) return;
v.push_back('"');
break;
case '/':
if(escaped) v.push_back('/');
case 'b':
if(escaped) v.push_back('\b');
case 'f':
if(escaped) v.push_back('\f');
case 'n':
if(escaped) v.push_back('\n');
case 'r':
if(escaped) v.push_back('\r');
case 't':
if(escaped) v.push_back('\t');
default:
if(escaped) escaped = false;
else v.push_back(c);
}
}
}
void String::serialize(ostream& out, size_t) const {
string s;
out << '"';
for(string::const_iterator it = v.begin(); it != v.end(); ++it) switch(*it) {
case '\b':
out << "\\b";
break;
case '\f':
out << "\\f";
break;
case '\n':
out << "\\n";
break;
case '\r':
out << "\\r";
break;
case '\t':
out << "\\t";
break;
case '\\':
case '"':
out << '\\';
default:
out << *it;
}
out << '"';
}
Boolean::Boolean(istream& in) {
string value;
while(in.peek() >= 'a' && in.peek() <= 'z')
value.push_back(in.get());
v = (value == "true" ? true : false);
}
}
#ifndef Bourne_h
#define Bourne_h
#include <string>
#include <map>
#include <vector>
#include <istream>
#include <ostream>
/**
* @brief Bourne, a JSON parser library
*/
namespace Bourne {
static const size_t IndentWidth = 4; /**< @brief Indentation width */
static const char IndentChar = ' '; /**< @brief Indentation character */
/** @brief Object type */
enum Type {
ObjectType, /**< @brief Object */
ArrayType, /**< @brief Array */
StringType, /**< @brief String */
IntegerType, /**< @brief Integer */
BooleanType /**< @brief Boolean */
};
class AbstractValue;
class Object;
class Array;
class String;
class Integer;
class Boolean;
/**
* @brief Null value
*/
static AbstractValue* Null = 0;
/**
* @brief Base class for all JSON datatypes (except null)
*/
class AbstractValue {
public:
/** @brief Destructor */
inline virtual ~AbstractValue() {}
/** @brief Object type */
virtual Type type() const = 0;
/**
* @brief Convert to object
* @return Object or 0, if this is not object
*/
inline Object* toObject() {
return type() == ObjectType ? reinterpret_cast<Object*>(this) : 0;
}
/**
* @brief Convert to array
* @return Array or 0, if this is not array
*/
inline Array* toArray() {
return type() == ArrayType ? reinterpret_cast<Array*>(this) : 0;
}
/**
* @brief Convert to string
* @return String or 0, if this is not string
*/
inline String* toString() {
return type() == StringType ? reinterpret_cast<String*>(this) : 0;
}
/**
* @brief Convert to integer
* @return Integer or 0, if this is not integer
*/
inline Integer* toInteger() {
return type() == IntegerType ? reinterpret_cast<Integer*>(this) : 0;
}
/**
* @brief Convert to boolean
* @return Boolean or 0, if this is not boolean
*/
inline Boolean* toBoolean() {
return type() == BooleanType ? reinterpret_cast<Boolean*>(this) : 0;
}
/**
* @brief Serialize the data to stream
* @param out Output stream
* @param indent Indentation width
*/
virtual void serialize(std::ostream& out, size_t indent = 0) const = 0;
protected:
static void ignorespace(std::istream& in);
static AbstractValue* deserialize(std::istream& in);
static void serialize(std::ostream& out, AbstractValue* value, size_t indent);
};
/** @brief Templated base class for simple datatypes */
template<class T> class _Datatype: public AbstractValue {
public:
/**
* @brief Default constructor
*
* Creates empty object
*/
inline _Datatype() {}
/**
* @brief Constructor
* @param value Value to store
*/
inline _Datatype(const T& value): v(value) {}
/**
* @brief Assignment operator
* @param value Value
*/
inline _Datatype& operator=(const T& value) { v = value; }
/** @fn T operator T() const
* @brief Cast to value type
* @return Value
*
* Convenience function. Instead of e.g.
* @code
* Integer i(42);
* int j = i.value();
* @endcode
* you can write
* @code
* Integer i(42);
* int j = i;
* @endcode
*/
inline operator T() const { return v; }
/**
* @brief Dereference
* @return Reference to value
*
* Convenience function. Instead of e.g.
* @code
* Integer i(42);
* i.set(7);
* @endcode
* you can write
* @code
* Integer i(42);
* *i = 15;
* @endcode
*/
inline T& operator*() const { return v; }
/** @brief Value */
inline T value() const { return v; }
/** @brief Set value */
inline void set(const T& value) { v = value; }
protected:
T v;
};
/** @brief String value */
class String: public _Datatype<std::string> {
public:
/** @copydoc _Datatype::_Datatype() */
inline String(const std::string& value): _Datatype(value) {}
inline String(const char* value): _Datatype(value) {}
String(std::istream& in);
inline Type type() const { return StringType; }
void serialize(std::ostream& out, size_t indent = 0) const;
};
/** @brief Integer value */
class Integer: public _Datatype<int> {
public:
/** @copydoc _Datatype::_Datatype() */
inline Integer(int value): _Datatype(value) {}
inline Integer(std::istream& in) {
in >> v;
}
inline Type type() const { return IntegerType; }
inline void serialize(std::ostream& out, size_t = 0) const {
out << v;
}
};
/** @brief String value */
class Boolean: public _Datatype<bool> {
public:
/** @copydoc _Datatype::_Datatype() */
inline Boolean(bool value): _Datatype(value) {}
Boolean(std::istream& in);
inline Type type() const { return BooleanType; }
inline void serialize(std::ostream& out, size_t = 0) const {
out << (v ? "true" : "false");
}
};
/**
* @brief Object
*
* Stores key/value pairs.
*/
class Object: public AbstractValue {
public:
/**
* @brief Default constructor
*
* Creates empty object
*/
inline Object() {}
/**
* @brief Constructor
* @param in Input stream
*
* Deserializes object from input stream.
*/
Object(std::istream& in);
/**
* @brief Destructor
*
* Deletes all key/value pairs from the object.
*/
~Object();
inline Type type() const { return ObjectType; }
/** @brief Count of key/value pairs in the object */
inline size_t size() const { return m.size(); }
/** @brief Whether the object contains given key */
inline bool contains(const std::string& key) const {
return m.find(key) != m.end();
}
/**
* @brief Get value at given key
* @param key Key
* @return Value or 0, if the value is null or there is no such key.
*
* See also contains().
*/
AbstractValue* at(const std::string& key) const;
/** @copydoc at() */
inline AbstractValue* operator[](const std::string& key) const {
return at(key);
}
/**
* @brief Get reference to value at given key
* @param key Key
* @return Reference to value
*
* Note that if given key is not found, it is automatically added.
*/
inline AbstractValue*& operator[](const std::string& key) {
return m[key];
}
/**
* @brief Add key/value pair to the object
* @param key Key
* @param value Value
* @return False if the key already exists, true otherwise.
*/
inline bool add(const std::string& key, AbstractValue* value) {
return m.insert(make_pair(key, value)).second;
}
inline void add(const std::string& key, const char* value) {
add(key, new String(value));
}
inline void add(const std::string& key, const std::string& value) {
add(key, new String(value));
}
inline void add(const std::string& key, int value) {
add(key, new Integer(value));
}
inline void add(const std::string& key, bool value) {
add(key, new Boolean(value));
}
/**
* @brief Remove key from the object
* @param key Key
* @return False if no element was removed, true otherwise.
*/
bool remove(const std::string& key);
/** @{ @name Iterator access */
typedef std::map<std::string, AbstractValue*>::iterator iterator;
typedef std::map<std::string, AbstractValue*>::const_iterator const_iterator;
inline iterator begin() { return m.begin(); }
inline const_iterator begin() const { return m.begin(); }
inline const_iterator cbegin() const { return m.begin(); }
inline iterator find(const std::string& key) { return m.find(key); }
inline const_iterator find(const std::string& key) const { return m.find(key); }
inline iterator end() { return m.end(); }
inline const_iterator end() const { return m.end(); }
inline const_iterator cend() const { return m.end(); }
/*@}*/
void serialize(std::ostream& out, size_t indent = 0) const;
private:
std::map<std::string, AbstractValue*> m;
};
/**
* @brief Array
*
* Stores ordered list of values.
*/
class Array: public AbstractValue {
public:
/**
* @brief Default constructor
*
* Creates empty array.
*/
inline Array() {}
/**
* @brief Destructor
*
* Deletes all elements from array.
*/
~Array();
/** @copydoc Object::Object() */
Array(std::istream& in);
inline Type type() const { return ArrayType; }
/** @brief Count of objects in the array */
inline size_t size() const { return a.size(); }
/**
* @brief Get value at given position
* @param index Index
* @return Value or 0, if the value is null
*/
inline AbstractValue* at(size_t index) const { return a[index]; }
/** @copydoc at() */
inline AbstractValue* operator[](size_t index) const { return at(index); }
/**
* @brief Add value to the array
* @param value Value
*/
inline void add(AbstractValue* value) { a.push_back(value); }
inline void add(const char* value) { add(new String(value)); }
inline void add(const std::string& value) { add(new String(value)); }
inline void add(int value) { add(new Integer(value)); }
inline void add(bool value) { add(new Boolean(value)); }
/**
* @copydoc add()
*
* Convenience function. Instead of e.g.
* @code
* Array a;
* a.add(42);
* a.add("foo");
* a.add(false)
* @endcode
* you can write
* @code
* Array a;
* a << 42 << "foo" << false;
* @endcode
*/
inline Array& operator<<(AbstractValue* value) {
add(value);
return *this;
}
inline Array& operator<<(const char* value) {
return operator<<(new String(value));
}
inline Array& operator<<(const std::string& value) {
return operator<<(new String(value));
}
inline Array& operator<<(int value) {
return operator<<(new Integer(value));
}
inline Array& operator<<(bool value) {
return operator<<(new Boolean(value));
}
/**
* @brief Remove value from the array
* @param index Index
*/
void remove(size_t index);
/** @{ @name Iterator access */
typedef std::vector<AbstractValue*>::iterator iterator;
typedef std::vector<AbstractValue*>::const_iterator const_iterator;
inline iterator begin() { return a.begin(); }
inline const_iterator begin() const { return a.begin(); }
inline const_iterator cbegin() const { return a.begin(); }
inline iterator end() { return a.end(); }
inline const_iterator end() const { return a.end(); }
inline const_iterator cend() const { return a.end(); }
/*@}*/
void serialize(std::ostream& out, size_t indent = 0) const;
private:
std::vector<AbstractValue*> a;
};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment