diff --git a/CMakeLists.txt b/CMakeLists.txt index be7d4c4..5ef831c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,17 +10,14 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) set(JSONBOX_SOURCES src/JsonWritingError.cpp src/Value.cpp - src/Array.cpp src/SolidusEscaper.cpp src/Escaper.cpp src/Indenter.cpp src/IndentCanceller.cpp src/JsonParsingError.cpp src/Convert.cpp - src/Object.cpp ) set(JSONBOX_HEADERS - include/JsonBox/Array.h include/JsonBox/Convert.h include/JsonBox/Escaper.h include/JsonBox/Grammar.h @@ -28,12 +25,10 @@ set(JSONBOX_HEADERS include/JsonBox/Indenter.h include/JsonBox/JsonParsingError.h include/JsonBox/JsonWritingError.h - include/JsonBox/Object.h include/JsonBox/OutputFilter.h include/JsonBox/SolidusEscaper.h include/JsonBox/Value.h include/JsonBox.h - ${CMAKE_CURRENT_BINARY_DIR}/Export.h ) # build library @@ -55,6 +50,14 @@ export(TARGETS JsonBox FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ) +# Macro used to install headers while keeping the directory hierarchy. +MACRO(INSTALL_HEADERS_WITH_DIRECTORY HEADER_LIST) + FOREACH(HEADER ${${HEADER_LIST}}) + STRING(REGEX MATCH "(.*)[/\\]" DIR ${HEADER}) + INSTALL(FILES ${HEADER} COMPONENT dev DESTINATION ${DIR}) + ENDFOREACH(HEADER) +ENDMACRO(INSTALL_HEADERS_WITH_DIRECTORY) + # install install(TARGETS JsonBox COMPONENT lib @@ -64,9 +67,10 @@ install(TARGETS JsonBox RUNTIME DESTINATION bin INCLUDES DESTINATION include ) -install(FILES ${JSONBOX_HEADERS} +INSTALL_HEADERS_WITH_DIRECTORY(JSONBOX_HEADERS) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Export.h COMPONENT dev - DESTINATION include + DESTINATION include/JsonBox ) install(EXPORT ${PROJECT_NAME} DESTINATION lib${LIB_SUFFIX}/cmake diff --git a/Doxyfile b/Doxyfile index b15c7de..30133bb 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ PROJECT_NAME = "Json Box" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.6.1 +PROJECT_NUMBER = 0.6.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/include/JsonBox.h b/include/JsonBox.h index e9290c8..06eee2c 100644 --- a/include/JsonBox.h +++ b/include/JsonBox.h @@ -25,7 +25,5 @@ */ #include -#include -#include #endif diff --git a/include/JsonBox/Array.h b/include/JsonBox/Array.h deleted file mode 100644 index ce76410..0000000 --- a/include/JsonBox/Array.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef JB_ARRAY_H -#define JB_ARRAY_H - -#include -#include - -#include "Value.h" - -namespace JsonBox { - JSONBOX_EXPORT std::ostream &operator<<(std::ostream &output, const Array &a); - - /** - * Represents an array of values in JSON. It's a vector with added methods. - * So it can be used the same way as a standard STL vector, but can be more - * easily output in a stream. - * @see JsonBox::Value - */ - class JSONBOX_EXPORT Array { - public: - typedef std::vector container; - typedef container::value_type value_type; - typedef container::allocator_type allocator_type; - typedef container::size_type size_type; - typedef container::difference_type difference_type; - typedef container::reference reference; - typedef container::const_reference const_reference; - typedef container::pointer pointer; - typedef container::const_pointer const_pointer; - typedef container::iterator iterator; - typedef container::const_iterator const_iterator; - typedef container::reverse_iterator reverse_iterator; - typedef container::const_reverse_iterator const_reverse_iterator; - - Array(const allocator_type &alloc = allocator_type()); - - explicit Array(size_type count, const_reference value = value_type(), const allocator_type &alloc = allocator_type()); - - template - Array(InputIterator first, InputIterator last, const allocator_type &alloc = allocator_type()) : data(first, last) { - } - - Array(const Array &other); - - Array &operator=(const Array &other); - - bool operator==(const Array &rhs) const; - - bool operator!=(const Array &rhs) const; - - bool operator<(const Array &rhs) const; - - bool operator<=(const Array &rhs) const; - - bool operator>(const Array &rhs) const; - - bool operator>=(const Array &rhs) const; - - void assign(size_type count, const_reference value); - - template - void assign(InputIterator first, InputIterator last) { - data.assign(first, last); - } - - allocator_type get_allocator() const; - - reference at(size_type pos); - - const_reference at(size_type pos) const; - - reference operator[](size_type pos); - - const_reference operator[](size_type pos) const; - - reference front(); - - const_reference front() const; - - reference back(); - - const_reference back() const; - - iterator begin(); - - const_iterator begin() const; - - iterator end(); - - const_iterator end() const; - - reverse_iterator rbegin(); - - const_reverse_iterator rbegin() const; - - reverse_iterator rend(); - - const_reverse_iterator rend() const; - - bool empty() const; - - size_type size() const; - - size_type max_size() const; - - void reserve(size_type size); - - size_type capacity() const; - - void clear(); - - iterator insert(iterator pos, const_reference value); - - void insert(iterator pos, size_type count, const_reference value); - - template - void insert(iterator pos, InputIterator first, InputIterator last) { - data.insert(pos, first, last); - } - - iterator erase(iterator pos); - - iterator erase(iterator first, iterator last); - - void push_back(const_reference value); - - void pop_back(); - - void resize(size_type count, const_reference value = value_type()); - - void swap(Array &other); - private: - container data; - }; - - /** - * Output operator overload for the JSON array. Outputs in standard JSON - * format. Indents the output and esapes the minimum characters. - * @param output Output stream in which to write the array's JSON. - * @param a Array to output into the stream. - * @return Output stream filled with the JSON code. - */ - std::ostream &operator<<(std::ostream &output, const Array &a); -} - -#endif diff --git a/include/JsonBox/Convert.h b/include/JsonBox/Convert.h index b326cfc..1377eaa 100644 --- a/include/JsonBox/Convert.h +++ b/include/JsonBox/Convert.h @@ -1,12 +1,13 @@ #ifndef JB_CONVERTER_H #define JB_CONVERTER_H +#include #include #include namespace JsonBox { - typedef std::basic_string String32; + typedef std::vector String32; /** * This class is used to encode/decode/transcode UTF8, 16 and 32. diff --git a/include/JsonBox/Object.h b/include/JsonBox/Object.h deleted file mode 100644 index 85b48b6..0000000 --- a/include/JsonBox/Object.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef JB_OBJECT_H -#define JB_OBJECT_H - -#include -#include -#include - -#include "Value.h" - -namespace JsonBox { - JSONBOX_EXPORT std::ostream &operator<<(std::ostream& output, const Object& o); - - /** - * Represents a JSON object. It's a map with added methods. So the JSON - * object type can be used the same way as a standard STL map of string and - * Value, but can be more easily output in a stream. - * @see JsonBox::Value - */ - class JSONBOX_EXPORT Object { - public: - typedef std::map container; - typedef container::key_type key_type; - typedef container::mapped_type mapped_type; - typedef container::value_type value_type; - typedef container::size_type size_type; - typedef container::difference_type difference_type; - typedef container::key_compare key_compare; - typedef container::allocator_type allocator_type; - typedef container::reference reference; - typedef container::const_reference const_reference; - typedef container::pointer pointer; - typedef container::const_pointer const_pointer; - typedef container::iterator iterator; - typedef container::const_iterator const_iterator; - typedef container::reverse_iterator reverse_iterator; - typedef container::const_reverse_iterator const_reverse_iterator; - - explicit Object(const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()); - - template - explicit Object(InputIterator first, InputIterator last, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : data(first, last, comp, alloc) { - } - - Object(const Object &other); - - Object &operator=(const Object &other); - - bool operator==(const Object &rhs) const; - - bool operator!=(const Object &rhs) const; - - bool operator<(const Object &rhs) const; - - bool operator<=(const Object &rhs) const; - - bool operator>(const Object &rhs) const; - - bool operator>=(const Object &rhs) const; - - allocator_type get_allocator() const; - - mapped_type &operator[](const key_type &key); - - iterator begin(); - - const_iterator begin() const; - - iterator end(); - - const_iterator end() const; - - reverse_iterator rbegin(); - - const_reverse_iterator rbegin() const; - - reverse_iterator rend(); - - const_reverse_iterator rend() const; - - bool empty() const; - - size_type size() const; - - size_type max_size() const; - - void clear(); - - std::pair insert(const_reference value); - - iterator insert(iterator hint, const_reference value); - - template - void insert(InputIterator first, InputIterator last) { - data.insert(first, last); - } - - void erase(iterator position); - - void erase(iterator first, iterator last); - - size_type erase(const key_type &key); - - void swap(Object &other); - - size_type count(const key_type &key) const; - - iterator find(const key_type &key); - - const_iterator find(const key_type &key) const; - - std::pair equal_range(const key_type &key); - - std::pair equal_range(const key_type &key) const; - - iterator lower_bound(const key_type &key); - - const_iterator lower_bound(const key_type &key) const; - - iterator upper_bound(const key_type &key); - - const_iterator upper_bound(const key_type &key) const; - - key_compare key_comp() const; - private: - container data; - }; - - /** - * Output operator overload for the JSON object. Outputs in standard JSON - * format. Indents the output and escapes the minimum characters. - * @param output Output stream in which to write the object's JSON. - * @param o Object to output into the stream. - * @return Output stream filled with the JSON code. - */ - std::ostream& operator<<(std::ostream& output, const Object& o); -} - -#endif diff --git a/include/JsonBox/Value.h b/include/JsonBox/Value.h index 243d5e2..1d53063 100644 --- a/include/JsonBox/Value.h +++ b/include/JsonBox/Value.h @@ -2,18 +2,13 @@ #define JB_VALUE_H #include +#include +#include #include #include "Export.h" - namespace JsonBox { - class Array; - class Object; - class Value; - - JSONBOX_EXPORT std::ostream &operator<<(std::ostream &output, const Value &v); - /** * Represents a json value. Can be a string, an integer, a floating point * number, an object, an array, a boolean value or a null value. To put it @@ -34,6 +29,8 @@ namespace JsonBox { */ friend std::ostream &operator<<(std::ostream &output, const Value &v); public: + typedef std::vector Array; + typedef std::map Object; /** * Represents the different types a value can be. A value can only be * one of these types at a time. The UNKNOWN type is only used @@ -534,8 +531,8 @@ namespace JsonBox { * @param output Output stream to write the value to. * @param indent Specifies if the output is to have nice indentation or * not. - * @param escapeAll Specifies if all the JSON escapable characters - * should be escaped or not. + * @param escapeAll Specifies whether or not all the JSON escapable + * characters should be escaped. * @see JsonBox::Value::operator<<(std::ostream& output, const Value& v) * @see JsonBox::Value::escapeAllCharacters * @see JsonBox::Value::escapeMinimumCharacters @@ -608,48 +605,6 @@ namespace JsonBox { ValueDataPointer(bool *newBoolValue); }; - /** - * Empty string returned by getString() when the value doesn't contain a - * string. - * @see JsonBox::Value::getString - */ - static const std::string EMPTY_STRING; - - /** - * Default int value returned by getInteger() when the value doesn't contain - * an integer. - * @see JsonBox::Value::getInt - */ - static const int EMPTY_INT = 0; - - /** - * Default double value returned by getDouble() when the value doesn't - * contain a double. - * @see JsonBox::Value::getDouble - */ - static const double EMPTY_DOUBLE; - - /** - * Default empty object value returned by getObject() when the value - * doesn't contain an object. - * @see JsonBox::Value::getObject - */ - static const Object EMPTY_OBJECT; - - /** - * Default empty array value returned by getArray() when the value - * doesn't contain an array. - * @see JsonBox::Value::getArray - */ - static const Array EMPTY_ARRAY; - - /** - * Default boolean value returned by getBoolean() when the value doesn't - * contain a boolean. - * @see JsonBox::Value::getBoolean - */ - static const bool EMPTY_BOOL = false; - /** * Checks if the char given is a hex digit. * @return True if the char contains an hexadecimal digit (0-9, a-f or @@ -733,6 +688,23 @@ namespace JsonBox { */ ValueDataPointer data; }; + + /** + * Represents an array of values in JSON. It's a STL vector that can be + * output in a stream. + * @see JsonBox::Value + */ + JSONBOX_EXPORT typedef std::vector Array; + + /** + * Represents a JSON object. It's a STL map that can be output in a stream. + * @see JsonBox::Value + */ + JSONBOX_EXPORT typedef std::map Object; + + JSONBOX_EXPORT std::ostream &operator<<(std::ostream &output, const Value &v); + JSONBOX_EXPORT std::ostream &operator<<(std::ostream &output, const Object &o); + JSONBOX_EXPORT std::ostream &operator<<(std::ostream &output, const Array &a); } #endif diff --git a/src/Array.cpp b/src/Array.cpp deleted file mode 100644 index 1f1fc35..0000000 --- a/src/Array.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace JsonBox { - Array::Array(const allocator_type &alloc) : data(alloc) { - } - - Array::Array(size_type count, const_reference value, const allocator_type &alloc) : data(count, value, alloc) { - } - - Array::Array(const Array &other) : data(other.data) { - } - - Array &Array::operator=(const Array &other) { - data = other.data; - return *this; - } - - bool Array::operator==(const Array &rhs) const { - return data == rhs.data; - } - - bool Array::operator!=(const Array &rhs) const { - return data != rhs.data; - } - - bool Array::operator<(const Array &rhs) const { - return data < rhs.data; - } - - bool Array::operator<=(const Array &rhs) const { - return data <= rhs.data; - } - - bool Array::operator>(const Array &rhs) const { - return data > rhs.data; - } - - bool Array::operator>=(const Array &rhs) const { - return data >= rhs.data; - } - - void Array::assign(size_type count, const_reference value) { - data.assign(count, value); - } - - Array::allocator_type Array::get_allocator() const { - return data.get_allocator(); - } - - Array::reference Array::at(size_type pos) { - return data.at(pos); - } - - Array::const_reference Array::at(size_type pos) const { - return data.at(pos); - } - - Array::reference Array::operator[](size_type pos) { - return data[pos]; - } - - Array::const_reference Array::operator[](size_type pos) const { - return data[pos]; - } - - Array::reference Array::front() { - return data.front(); - } - - Array::const_reference Array::front() const { - return data.front(); - } - - Array::reference Array::back() { - return data.back(); - } - - Array::const_reference Array::back() const { - return data.back(); - } - - Array::iterator Array::begin() { - return data.begin(); - } - - Array::const_iterator Array::begin() const { - return data.begin(); - } - - Array::iterator Array::end() { - return data.end(); - } - - Array::const_iterator Array::end() const { - return data.end(); - } - - Array::reverse_iterator Array::rbegin() { - return data.rbegin(); - } - - Array::const_reverse_iterator Array::rbegin() const { - return data.rbegin(); - } - - Array::reverse_iterator Array::rend() { - return data.rend(); - } - - Array::const_reverse_iterator Array::rend() const { - return data.rend(); - } - - bool Array::empty() const { - return data.empty(); - } - - Array::size_type Array::size() const { - return data.size(); - } - - Array::size_type Array::max_size() const { - return data.max_size(); - } - - void Array::reserve(size_type size) { - data.reserve(size); - } - - Array::size_type Array::capacity() const { - return data.capacity(); - } - - void Array::clear() { - data.clear(); - } - - Array::iterator Array::insert(iterator pos, const_reference value) { - return data.insert(pos, value); - } - - void Array::insert(iterator pos, size_type count, const_reference value) { - data.insert(pos, count, value); - } - - Array::iterator Array::erase(iterator pos) { - return data.erase(pos); - } - - Array::iterator Array::erase(iterator first, iterator last) { - return data.erase(first, last); - } - - void Array::push_back(const_reference value) { - data.push_back(value); - } - - void Array::pop_back() { - data.pop_back(); - } - - void Array::resize(size_type count, const_reference value) { - data.resize(count, value); - } - - void Array::swap(Array &other) { - data.swap(other.data); - } - - std::ostream &operator<<(std::ostream &output, const Array &a) { - if (a.empty()) { - output << Structural::BEGIN_ARRAY << Structural::END_ARRAY; - - } else { - output << Structural::BEGIN_ARRAY << std::endl; - OutputFilter indent(output.rdbuf()); - output.rdbuf(&indent); - - for (Array::const_iterator i = a.begin(); i != a.end(); ++i) { - if (i != a.begin()) { - output << Structural::VALUE_SEPARATOR << std::endl; - } - - output << *i; - } - - output.rdbuf(indent.getDestination()); - - output << std::endl << Structural::END_ARRAY; - } - - return output; - } -} diff --git a/src/Object.cpp b/src/Object.cpp deleted file mode 100644 index ae958a8..0000000 --- a/src/Object.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace JsonBox { - Object::Object(const key_compare &comp, const allocator_type &alloc) : data(comp, alloc) { - } - - Object::Object(const Object &other) : data(other.data) { - } - - Object &Object::operator=(const Object &other) { - data = other.data; - return *this; - } - - bool Object::operator==(const Object &rhs) const { - return data == rhs.data; - } - - bool Object::operator!=(const Object &rhs) const { - return data != rhs.data; - } - - bool Object::operator<(const Object &rhs) const { - return data < rhs.data; - } - - bool Object::operator<=(const Object &rhs) const { - return data <= rhs.data; - } - - bool Object::operator>(const Object &rhs) const { - return data > rhs.data; - } - - bool Object::operator>=(const Object &rhs) const { - return data >= rhs.data; - } - - Object::allocator_type Object::get_allocator() const { - return data.get_allocator(); - } - - Object::mapped_type &Object::operator[](const key_type &key) { - return data[key]; - } - - Object::iterator Object::begin() { - return data.begin(); - } - - Object::const_iterator Object::begin() const { - return data.begin(); - } - - Object::iterator Object::end() { - return data.end(); - } - - Object::const_iterator Object::end() const { - return data.end(); - } - - Object::reverse_iterator Object::rbegin() { - return data.rbegin(); - } - - Object::const_reverse_iterator Object::rbegin() const { - return data.rbegin(); - } - - Object::reverse_iterator Object::rend() { - return data.rend(); - } - - Object::const_reverse_iterator Object::rend() const { - return data.rend(); - } - - bool Object::empty() const { - return data.empty(); - } - - Object::size_type Object::size() const { - return data.size(); - } - - Object::size_type Object::max_size() const { - return data.max_size(); - } - - void Object::clear() { - data.clear(); - } - - std::pair Object::insert(const_reference value) { - return data.insert(value); - } - - Object::iterator Object::insert(iterator hint, const_reference value) { - return data.insert(hint, value); - } - - void Object::erase(iterator position) { - data.erase(position); - } - - void Object::erase(iterator first, iterator last) { - data.erase(first, last); - } - - Object::size_type Object::erase(const key_type &key) { - return data.erase(key); - } - - void Object::swap(Object &other) { - data.swap(other.data); - } - - Object::size_type Object::count(const key_type &key) const { - return data.count(key); - } - - Object::iterator Object::find(const key_type &key) { - return data.find(key); - } - - Object::const_iterator Object::find(const key_type &key) const { - return data.find(key); - } - - std::pair Object::equal_range(const key_type &key) { - return data.equal_range(key); - } - - std::pair Object::equal_range(const key_type &key) const { - return data.equal_range(key); - } - - Object::iterator Object::lower_bound(const key_type &key) { - return data.lower_bound(key); - } - - Object::const_iterator Object::lower_bound(const key_type &key) const { - return data.lower_bound(key); - } - - Object::iterator Object::upper_bound(const key_type &key) { - return data.upper_bound(key); - } - - Object::const_iterator Object::upper_bound(const key_type &key) const { - return data.upper_bound(key); - } - - Object::key_compare Object::key_comp() const { - return data.key_comp(); - } - - std::ostream &operator<<(std::ostream &output, const Object &o) { - // If the object is empty, we simply write "{}". - if (o.empty()) { - output << Structural::BEGIN_OBJECT << Structural::END_OBJECT; - - } else { - output << Structural::BEGIN_OBJECT << std::endl; - OutputFilter indent(output.rdbuf()); - output.rdbuf(&indent); - - // For each item in the object. - for (Object::const_iterator i = o.begin(); i != o.end(); ++i) { - if (i != o.begin()) { - output << Structural::VALUE_SEPARATOR << std::endl; - } - - // We print the name of the attribute and its value. - output << Structural::BEGIN_END_STRING << Value::escapeMinimumCharacters(i->first) << Structural::BEGIN_END_STRING << Whitespace::SPACE << Structural::NAME_SEPARATOR << Whitespace::SPACE << i->second; - } - - output.rdbuf(indent.getDestination()); - - output << std::endl << Structural::END_OBJECT; - } - - return output; - } -} diff --git a/src/Value.cpp b/src/Value.cpp index 78ebc17..a44badc 100755 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -11,19 +11,55 @@ #include #include #include +#include #include #include -#include -#include #include #include namespace JsonBox { - const std::string Value::EMPTY_STRING = std::string(); - const double Value::EMPTY_DOUBLE = 0.0; - const Object Value::EMPTY_OBJECT = Object(); - const Array Value::EMPTY_ARRAY = Array(); + /** + * Empty string returned by getString() when the value doesn't contain a + * string. + * @see JsonBox::Value::getString + */ + static const std::string EMPTY_STRING = std::string(); + + /** + * Default int value returned by getInteger() when the value doesn't contain + * an integer. + * @see JsonBox::Value::getInt + */ + static const int EMPTY_INT = 0; + + /** + * Default double value returned by getDouble() when the value doesn't + * contain a double. + * @see JsonBox::Value::getDouble + */ + static const double EMPTY_DOUBLE = 0.0; + + /** + * Default empty object value returned by getObject() when the value + * doesn't contain an object. + * @see JsonBox::Value::getObject + */ + static const Object EMPTY_OBJECT = Object(); + + /** + * Default empty array value returned by getArray() when the value + * doesn't contain an array. + * @see JsonBox::Value::getArray + */ + static const Array EMPTY_ARRAY = Array(); + + /** + * Default boolean value returned by getBoolean() when the value doesn't + * contain a boolean. + * @see JsonBox::Value::getBoolean + */ + static const bool EMPTY_BOOL = false; std::string Value::escapeMinimumCharacters(const std::string &str) { std::stringstream result; @@ -446,16 +482,16 @@ namespace JsonBox { } else { switch (type) { case INTEGER: { - std::stringstream ss; - ss << *data.intValue; - return ss.str(); - } + std::stringstream ss; + ss << *data.intValue; + return ss.str(); + } case DOUBLE: { - std::stringstream ss; - ss << *data.doubleValue; - return ss.str(); - } + std::stringstream ss; + ss << *data.doubleValue; + return ss.str(); + } case BOOLEAN: return (*data.boolValue) ? (Literals::TRUE_STRING) : (Literals::FALSE_STRING); @@ -1144,8 +1180,7 @@ namespace JsonBox { output << v.getInteger(); break; - case Value::DOUBLE: - { + case Value::DOUBLE: { std::streamsize precisionBackup = output.precision(); output.precision(17); output << v.getDouble(); @@ -1175,4 +1210,57 @@ namespace JsonBox { return output; } + + std::ostream &operator<<(std::ostream &output, const Array &a) { + if (a.empty()) { + output << Structural::BEGIN_ARRAY << Structural::END_ARRAY; + + } else { + output << Structural::BEGIN_ARRAY << std::endl; + OutputFilter indent(output.rdbuf()); + output.rdbuf(&indent); + + for (Array::const_iterator i = a.begin(); i != a.end(); ++i) { + if (i != a.begin()) { + output << Structural::VALUE_SEPARATOR << std::endl; + } + + output << *i; + } + + output.rdbuf(indent.getDestination()); + + output << std::endl << Structural::END_ARRAY; + } + + return output; + } + + std::ostream &operator<<(std::ostream &output, const Object &o) { + // If the object is empty, we simply write "{}". + if (o.empty()) { + output << Structural::BEGIN_OBJECT << Structural::END_OBJECT; + + } else { + output << Structural::BEGIN_OBJECT << std::endl; + OutputFilter indent(output.rdbuf()); + output.rdbuf(&indent); + + // For each item in the object. + for (Object::const_iterator i = o.begin(); i != o.end(); ++i) { + if (i != o.begin()) { + output << Structural::VALUE_SEPARATOR << std::endl; + } + + // We print the name of the attribute and its value. + output << Structural::BEGIN_END_STRING << Value::escapeMinimumCharacters(i->first) << Structural::BEGIN_END_STRING << Whitespace::SPACE << Structural::NAME_SEPARATOR << Whitespace::SPACE << i->second; + } + + output.rdbuf(indent.getDestination()); + + output << std::endl << Structural::END_OBJECT; + } + + return output; + } }