Created
November 21, 2011 22:26
-
-
Save mosra/1384168 to your computer and use it in GitHub Desktop.
Bourne, a JSON parser library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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