changeset 314:4c3019385769

Json: add iterators TODO: - Test operator->, const_iterator must not let modify - Test const and non const operator[]
author David Demelier <markand@malikania.fr>
date Wed, 25 Feb 2015 13:53:41 +0100
parents cd490a8ab82a
children c9356cb38c86 b67b2da45bb0
files C++/Json.cpp C++/Json.h C++/Tests/Json/main.cpp
diffstat 3 files changed, 1842 insertions(+), 829 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Json.cpp	Wed Feb 25 12:42:06 2015 +0100
+++ b/C++/Json.cpp	Wed Feb 25 13:53:41 2015 +0100
@@ -16,177 +16,34 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <stdexcept>
+
 #include "Json.h"
 
 /* --------------------------------------------------------
- * JsonValue implementation
+ * JsonObject
  * -------------------------------------------------------- */
 
-JsonValue::JsonValue(const JsonValue &value)
-	: m_handle{json_deep_copy(value.m_handle.get()), json_decref}
-{
-}
-
-JsonValue &JsonValue::operator=(const JsonValue &value)
-{
-	m_handle = {json_deep_copy(value.m_handle.get()), json_decref};
-}
-
-JsonValue::JsonValue(json_t *json)
-	: m_handle{json, json_decref}
-{
-}
-
-JsonValue::JsonValue()
-	: m_handle{json_null(), json_decref}
-{
-}
-
-JsonValue::JsonValue(bool value)
-	: m_handle{json_boolean(value), json_decref}
-{
-}
-
-JsonValue::JsonValue(int value)
-	: m_handle{json_integer(value), json_decref}
-{
-}
-
-JsonValue::JsonValue(double value)
-	: m_handle{json_real(value), json_decref}
-{
-}
-
-JsonValue::JsonValue(std::string value)
-	: m_handle{json_string(value.c_str()), json_decref}
-{
-}
-
-JsonValue::JsonValue(const char *value)
-	: m_handle{json_string(value), json_decref}
-{
-}
-
-JsonType JsonValue::typeOf() const
-{
-	return static_cast<JsonType>(json_typeof(m_handle.get()));
-}
-
-bool JsonValue::isObject() const
-{
-	return json_is_object(m_handle.get()) != 0;
-}
-
-bool JsonValue::isArray() const
-{
-	return json_is_array(m_handle.get()) != 0;
-}
-
-bool JsonValue::isString() const
-{
-	return json_is_string(m_handle.get()) != 0;
-}
-
-bool JsonValue::isReal() const
-{
-	return json_is_real(m_handle.get()) != 0;
-}
-
-bool JsonValue::isTrue() const
-{
-	return json_is_true(m_handle.get()) != 0;
-}
-
-bool JsonValue::isFalse() const
-{
-	return json_is_false(m_handle.get()) != 0;
-}
-
-bool JsonValue::isNull() const
-{
-	return json_is_null(m_handle.get()) != 0;
-}
-
-bool JsonValue::isNumber() const
-{
-	return json_is_number(m_handle.get()) != 0;
-}
-
-bool JsonValue::isInteger() const
-{
-	return json_is_integer(m_handle.get()) != 0;
-}
-
-bool JsonValue::isBoolean() const
-{
-	return json_is_boolean(m_handle.get()) != 0;
-}
-
-std::string JsonValue::toString() const
-{
-	auto value = json_string_value(m_handle.get());
-
-	return (value == nullptr) ? "" : value;
-}
-
-int JsonValue::toInteger() const noexcept
-{
-	return json_integer_value(m_handle.get());
-}
-
-double JsonValue::toReal() const noexcept
-{
-	return json_real_value(m_handle.get());
-}
-
-JsonObject JsonValue::toObject() const
+JsonObject JsonValue::toObject() const noexcept
 {
 	json_incref(m_handle.get());
 
-	return JsonObject{m_handle.get()};
+	return JsonObject(m_handle.get());
 }
 
-JsonArray JsonValue::toArray() const
+JsonArray JsonValue::toArray() const noexcept
 {
 	json_incref(m_handle.get());
 
-	return JsonArray{m_handle.get()};
+	return JsonArray(m_handle.get());
 }
 
 /* --------------------------------------------------------
  * JsonArray
  * -------------------------------------------------------- */
 
-JsonArray::JsonArray()
-	: JsonValue{json_array()}
-{
-}
-
-unsigned JsonArray::size() const noexcept
-{
-	return json_array_size(m_handle.get());
-}
-
-void JsonArray::push(const JsonValue &value)
+JsonValue JsonArray::at(int index) const
 {
-	json_array_insert(m_handle.get(), 0, value.m_handle.get());
-}
-
-void JsonArray::append(const JsonValue &value)
-{
-	json_array_append(m_handle.get(), value.m_handle.get());
-}
-
-void JsonArray::insert(const JsonValue &value, int index)
-{
-	json_array_insert(m_handle.get(), index, value.m_handle.get());
-}
-
-JsonValue JsonArray::operator[](int index) const
-{
-	if (typeOf() != JsonType::Array)
-		throw JsonError{"not an array"};
-
 	auto value = json_array_get(m_handle.get(), index);
 
 	if (value == nullptr)
@@ -197,66 +54,105 @@
 	return JsonValue{value};
 }
 
+JsonValue JsonArray::operator[](int index) const noexcept
+{
+	auto value = json_array_get(m_handle.get(), index);
+
+	if (value == nullptr)
+		return JsonValue();
+
+	json_incref(value);
+
+	return JsonValue(value);
+}
+
+JsonArray::Ref JsonArray::operator[](int index) noexcept
+{
+	auto value = json_array_get(m_handle.get(), index);
+
+	if (value == nullptr)
+		value = json_null();
+	else
+		json_incref(value);
+
+	return Ref(value, *this, index);
+}
+
 /* --------------------------------------------------------
  * JsonObject
  * -------------------------------------------------------- */
 
-JsonObject::JsonObject()
-	: JsonValue{json_object()}
+JsonObject::Ref JsonObject::operator[](const std::string &name)
 {
+	if (typeOf() != JsonType::Object)
+		return Ref(JsonValue(), *this, name);
+
+	auto value = json_object_get(m_handle.get(), name.c_str());
+
+	json_incref(value);
+
+	return Ref(value, *this, name);
 }
 
 JsonValue JsonObject::operator[](const std::string &name) const
 {
 	if (typeOf() != JsonType::Object)
-		throw JsonError{"not an object"};
+		return JsonValue();
 
 	auto value = json_object_get(m_handle.get(), name.c_str());
 
 	if (value == nullptr)
-		throw JsonError{"key " + name + +" not found"};
+		return JsonValue();
 
 	json_incref(value);
 
-	return JsonValue{value};
-}
-
-void JsonObject::set(const std::string &key, const JsonValue &value)
-{
-	json_object_set(m_handle.get(), key.c_str(), value.m_handle.get());
+	return JsonValue(value);
 }
 
 /* --------------------------------------------------------
- * JsonReaderFile
+ * JsonDocument
  * -------------------------------------------------------- */
 
-JsonReaderFile::JsonReaderFile(std::string path)
-	: m_path{std::move(path)}
-{
-}
-
-JsonValue JsonReaderFile::read()
+JsonValue JsonDocument::read(std::string content, int flags) const
 {
 	json_error_t error;
-	json_t *handle = json_load_file(m_path.c_str(), 0, &error);
+	json_t *json = json_loads(content.c_str(), flags, &error);
 
-	if (handle == nullptr)
-		throw JsonError{error};
+	if (json == nullptr)
+		throw JsonError(error);
 
-	return JsonValue{handle};
+	return JsonValue(json);
 }
 
-/* --------------------------------------------------------
- * JsonWriterFile
- * -------------------------------------------------------- */
+JsonValue JsonDocument::read(std::ifstream &stream, int flags) const
+{
+	if (!stream.is_open())
+		throw JsonError("File not opened");
 
-JsonWriterFile::JsonWriterFile(std::string path)
-	: m_path{std::move(path)}
-{
+	stream.seekg(0, stream.end);
+	auto length = stream.tellg();
+	stream.seekg(0, stream.beg);
+
+	std::string buffer;
+	buffer.resize(length, ' ');
+
+	stream.read(&buffer[0], length);
+	stream.close();
+
+	return read(std::move(buffer), flags);
 }
 
-void JsonWriterFile::write(const JsonValue &value)
+JsonDocument::JsonDocument(std::ifstream &stream, int flags)
+{
+	m_value = read(stream, flags);
+}
+
+JsonDocument::JsonDocument(std::ifstream &&stream, int flags)
 {
-	if (json_dump_file(value, m_path.c_str(), 0) < 0)
-		throw JsonError{"Failed to write file: " + m_path};
+	m_value = read(stream, flags);
 }
+
+JsonDocument::JsonDocument(std::string content, int flags)
+{
+	m_value = read(std::move(content), flags);
+}
\ No newline at end of file
--- a/C++/Json.h	Wed Feb 25 12:42:06 2015 +0100
+++ b/C++/Json.h	Wed Feb 25 13:53:41 2015 +0100
@@ -19,8 +19,12 @@
 #ifndef _JSON_H_
 #define _JSON_H_
 
+#include <algorithm>
+#include <cstdlib>
+#include <initializer_list>
+#include <fstream>
+#include <iterator>
 #include <memory>
-#include <stdexcept>
 #include <string>
 #include <utility>
 
@@ -56,390 +60,12 @@
 	Null = JSON_NULL		//!< Empty or null
 };
 
-class JsonObject;
-class JsonArray;
-
-/**
- * @class JsonValue
- * @brief Encapsulate any JSON value
- */
-class JsonValue {
-public:
-	using Handle = std::unique_ptr<json_t, void (*)(json_t *)>;
-
-	friend class JsonObject;
-	friend class JsonArray;
-	friend class JsonDocument;
-
-protected:
-	/**
-	 * The unique_ptr handle of json_t, will automatically decrease
-	 * the reference count in its deleter.
-	 */
-	Handle m_handle;
-
-public:
-	/**
-	 * Create a JsonValue from a native Jansson type. This function
-	 * will increment the json_t reference count.
-	 *
-	 * @param json the value
-	 */
-	JsonValue(json_t *json);
-
-	/**
-	 * Deep copy of that element.
-	 *
-	 * @param value the other value
-	 */
-	JsonValue(const JsonValue &value);
-
-	/**
-	 * Assign a deep copy of the other element.
-	 *
-	 * @return *this
-	 */
-	JsonValue &operator=(const JsonValue &);
-
-	/**
-	 * Default move constructor.
-	 *
-	 * @param other the other value
-	 */
-	JsonValue(JsonValue &&other) = default;
-
-	/**
-	 * Default move assignment.
-	 *
-	 * @param other the other value
-	 */
-	JsonValue &operator=(JsonValue &&) = default;
-
-	/**
-	 * Create an empty value (JsonType::Null).
-	 */
-	JsonValue();
-
-	/**
-	 * Create a boolean value.
-	 *
-	 * @param value the value
-	 */
-	JsonValue(bool value);
-
-	/**
-	 * Create a integer value (JsonType::Integer).
-	 *
-	 * @param value the value
-	 */
-	JsonValue(int value);
-
-	/**
-	 * Create a real value (JsonType::Real).
-	 *
-	 * @param value the value
-	 */
-	JsonValue(double value);
-
-	/**
-	 * Create a string value (JsonType::String).
-	 * @param value
-	 */
-	JsonValue(std::string value);
-
-	/**
-	 * Create from a C string (JsonType::String).
-	 *
-	 * @param value the string
-	 */
-	JsonValue(const char *value);
-
-	/**
-	 * Create from a string literal (JsonType::String).
-	 *
-	 * @param value the value
-	 */
-	template <size_t Size>
-	inline JsonValue(char (&value)[Size])
-		: m_handle{json_string(value), json_decref}
-	{
-	}
-
-	/**
-	 * Default destructor.
-	 */
-	virtual ~JsonValue() = default;
-
-	/**
-	 * Get the type of value.
-	 *
-	 * @return the type
-	 */
-	JsonType typeOf() const;
-
-	/**
-	 * Tells if the json value is an JSON_OBJECT.
-	 *
-	 * @return true or false
-	 */
-	bool isObject() const;
-
-	/**
-	 * Tells if the json value is an JSON_ARRAY.
-	 *
-	 * @return true or false
-	 */
-	bool isArray() const;
-
-	/**
-	 * Tells if the json value is an JSON_STRING.
-	 *
-	 * @return true or false
-	 */
-	bool isString() const;
-
-	/**
-	 * Tells if the json value is an JSON_REAL.
-	 *
-	 * @return true or false
-	 */
-	bool isReal() const;
-
-	/**
-	 * Tells if the json value is an JSON_TRUE.
-	 *
-	 * @return true or false
-	 */
-	bool isTrue() const;
-
-	/**
-	 * Tells if the json value is an JSON_FALSE.
-	 *
-	 * @return true or false
-	 */
-	bool isFalse() const;
-
-	/**
-	 * Tells if the json value is an JSON_NULL.
-	 *
-	 * @return true or false
-	 */
-	bool isNull() const;
-
-	/**
-	 * Tells if the json value is an JSON_INTEGER or JSON_REAL.
-	 *
-	 * @return true or false
-	 */
-	bool isNumber() const;
-
-	/**
-	 * Tells if the json value is an JSON_INTEGER.
-	 *
-	 * @return true or false
-	 */
-	bool isInteger() const;
-
-	/**
-	 * Tells if the json value is an JSON_TRUE or JSON_FALSE.
-	 *
-	 * @return true or false
-	 */
-	bool isBoolean() const;
-
-	/**
-	 * Get the string value.
-	 *
-	 * @return the string
-	 */
-	std::string toString() const;
-
-	/**
-	 * Get the integer value.
-	 *
-	 * @return the value or 0
-	 */
-	int toInteger() const noexcept;
-
-	/**
-	 * Get the real value.
-	 *
-	 * @return the value or 0
-	 */
-	double toReal() const noexcept;
-
-	/**
-	 * Convert to object.
-	 *
-	 * @return an object
-	 */
-	JsonObject toObject() const;
-
-	/**
-	 * Convert to array.
-	 *
-	 * @return an array
-	 */
-	JsonArray toArray() const;
-
-	/**
-	 * Convert to native Jansson type.
-	 *
-	 * You should not call json_incref or json_decref on it as it is
-	 * automatically done.
-	 *
-	 * @return the json_t handle
-	 * @warning use this function with care
-	 */
-	inline operator json_t *() noexcept
-	{
-		return m_handle.get();
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the json_t handle
-	 */
-	inline operator const json_t *() const noexcept
-	{
-		return m_handle.get();
-	}
-};
-
-/**
- * @class JsonArray
- * @brief Manipulate JSON arrays
- */
-class JsonArray final : public JsonValue {
-public:
-	using JsonValue::JsonValue;
-
-	/**
-	 * Create an empty array.
-	 */
-	JsonArray();
-
-	/**
-	 * Get the number of values in the array
-	 *
-	 * @return the number or 0
-	 */
-	unsigned size() const noexcept;
-
-	/**
-	 * Insert the value at the beginning.
-	 *
-	 * @param value the value
-	 */
-	void push(const JsonValue &value);
-
-	/**
-	 * Insert a copy of the value at the end.
-	 *
-	 * @param value the value to insert
-	 */
-	void append(const JsonValue &value);
-
-	/**
-	 * Insert a copy of the value at the specified index.
-	 *
-	 * @param value the value to insert
-	 * @param index the position
-	 */
-	void insert(const JsonValue &value, int index);
-
-	/**
-	 * Get the value at the specified index.
-	 *
-	 * @param index the position
-	 * @return the value
-	 * @throw std::invalid_argument on error
-	 * @throw std::out_of_range on bad arguments
-	 */
-	JsonValue operator[](int index) const;
-
-	/**
-	 * Iterate over the array value (not recursively).
-	 *
-	 * The function must have the following signature:
-	 *    void f(int, const JsonValue &)
-	 *
-	 * @param function the function to call
-	 */
-	template <typename Func>
-	void forAll(Func function) const
-	{
-		json_t *value;
-		size_t index;
-
-		json_array_foreach(m_handle.get(), index, value) {
-			json_incref(value);
-			function(static_cast<int>(index), JsonValue{value});
-		}
-	}
-};
-
-/**
- * @class JsonObject
- * @brief Object wrapper
- */
-class JsonObject final : public JsonValue {
-public:
-	using JsonValue::JsonValue;
-
-	/**
-	 * Create empty object.
-	 */
-	JsonObject();
-
-	/**
-	 * Get the value at the specified key. The returned value is a borrowed reference,
-	 *
-	 * @param key the key
-	 * @return the value
-	 * @throw std::invalid_argument on error
-	 * @throw std::out_of_range on bad arguments
-	 */
-	JsonValue operator[](const std::string &key) const;
-
-	/**
-	 * Set the value as key in the object.
-	 *
-	 * @param key the key
-	 * @param value the value
-	 */
-	void set(const std::string &key, const JsonValue &value);
-
-	/**
-	 * Iterate over the object keys (not recursively).
-	 *
-	 * The function must have the following signature:
-	 *    void f(const std::string &key, const JsonValue &)
-	 *
-	 * @param function the function to call
-	 */
-	template <typename Func>
-	void forAll(Func function) const
-	{
-		json_t *value;
-		const char *key;
-
-		json_object_foreach(m_handle.get(), key, value) {
-			json_incref(value);
-			function(std::string(key), JsonValue{value});
-		}
-	}
-};
-
 /**
  * @class JsonError
  * @brief Error thrown for any error
  */
 class JsonError final : public std::exception {
 private:
-	friend class Json;
-
 	std::string	m_text;
 	std::string	m_source;
 	int		m_line{};
@@ -532,104 +158,1312 @@
 	}
 };
 
+class JsonObject;
+class JsonArray;
+
 /**
- * @class JsonReader
- * @brief Base class for JSON reading
+ * @class JsonValue
+ * @brief Encapsulate any JSON value
  */
-class JsonReader {
+class JsonValue {
+public:
+	using Handle = std::unique_ptr<json_t, void (*)(json_t *)>;
+
+	friend class JsonObject;
+	friend class JsonArray;
+
+protected:
+	/**
+	 * The unique_ptr handle of json_t, will automatically decrease
+	 * the reference count in its deleter.
+	 */
+	Handle m_handle;
+
 public:
 	/**
-	 * Default constructor.
+	 * Create a JsonValue from a native Jansson type. This function
+	 * will increment the json_t reference count.
+	 *
+	 * @param json the value
 	 */
-	JsonReader() = default;
+	inline JsonValue(json_t *json) noexcept
+		: m_handle{json, json_decref}
+	{
+	}
 
 	/**
-	 * Default destructor.
+	 * Deep copy of that element.
+	 *
+	 * @param value the other value
+	 */
+	inline JsonValue(const JsonValue &value)
+		: m_handle{json_deep_copy(value.m_handle.get()), json_decref}
+	{
+	}
+
+	/**
+	 * Assign a deep copy of the other element.
+	 *
+	 * @return *this
 	 */
-	virtual ~JsonReader() = default;
+	inline JsonValue &operator=(const JsonValue &value)
+	{
+		m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref);
+
+		return *this;
+	}
+
+	/**
+	 * Default move constructor.
+	 *
+	 * @param other the other value
+	 */
+	JsonValue(JsonValue &&other) = default;
 
 	/**
-	 * Read the source and return a value.
+	 * Default move assignment.
+	 *
+	 * @param other the other value
+	 */
+	JsonValue &operator=(JsonValue &&) = default;
+
+	/**
+	 * Create an empty value (JsonType::Null).
+	 */
+	inline JsonValue()
+		: m_handle(json_null(), json_decref)
+	{
+	}
+
+	/**
+	 * Create a boolean value.
 	 *
-	 * @return a value
+	 * @param value the value
 	 */
-	virtual JsonValue read() = 0;
-};
+	inline JsonValue(bool value)
+		: m_handle(json_boolean(value), json_decref)
+	{
+	}
+
+	/**
+	 * Create a integer value (JsonType::Integer).
+	 *
+	 * @param value the value
+	 */
+	inline JsonValue(int value)
+		: m_handle(json_integer(value), json_decref)
+	{
+	}
 
-/**
- * @class JsonWriter
- * @brief Base class for writing
- */
-class JsonWriter {
-public:
+	/**
+	 * Create a real value (JsonType::Real).
+	 *
+	 * @param value the value
+	 */
+	inline JsonValue(double value)
+		: m_handle(json_real(value), json_decref)
+	{
+	}
+
+	/**
+	 * Create a string value (JsonType::String).
+	 * @param value
+	 */
+	inline JsonValue(std::string value)
+		: m_handle(json_string(value.c_str()), json_decref)
+	{
+	}
+
 	/**
-	 * Default destructor.
+	 * Create from a C string (JsonType::String).
+	 *
+	 * @param value the string
 	 */
-	JsonWriter() = default;
+	inline JsonValue(const char *value)
+		: m_handle(json_string(value), json_decref)
+	{
+	}
+
+	/**
+	 * Create from a string literal (JsonType::String).
+	 *
+	 * @param value the value
+	 */
+	template <size_t Size>
+	inline JsonValue(char (&value)[Size])
+		: m_handle{json_string(value), json_decref}
+	{
+	}
 
 	/**
 	 * Default destructor.
 	 */
-	virtual ~JsonWriter() = default;
+	virtual ~JsonValue() = default;
+
+	/**
+	 * Get the type of value.
+	 *
+	 * @return the type
+	 */
+	inline JsonType typeOf() const noexcept
+	{
+		return static_cast<JsonType>(json_typeof(m_handle.get()));
+	}
+
+	/**
+	 * Tells if the json value is an JSON_OBJECT.
+	 *
+	 * @return true or false
+	 */
+	inline bool isObject() const noexcept
+	{
+		return json_is_object(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_ARRAY.
+	 *
+	 * @return true or false
+	 */
+	inline bool isArray() const noexcept
+	{
+		return json_is_array(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_STRING.
+	 *
+	 * @return true or false
+	 */
+	inline bool isString() const noexcept
+	{
+		return json_is_string(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_REAL.
+	 *
+	 * @return true or false
+	 */
+	inline bool isReal() const noexcept
+	{
+		return json_is_real(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_TRUE.
+	 *
+	 * @return true or false
+	 */
+	inline bool isTrue() const noexcept
+	{
+		return json_is_true(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_FALSE.
+	 *
+	 * @return true or false
+	 */
+	inline bool isFalse() const noexcept
+	{
+		return json_is_false(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_NULL.
+	 *
+	 * @return true or false
+	 */
+	inline bool isNull() const noexcept
+	{
+		return json_is_null(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_INTEGER or JSON_REAL.
+	 *
+	 * @return true or false
+	 */
+	inline bool isNumber() const noexcept
+	{
+		return json_is_number(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_INTEGER.
+	 *
+	 * @return true or false
+	 */
+	inline bool isInteger() const noexcept
+	{
+		return json_is_integer(m_handle.get());
+	}
+
+	/**
+	 * Tells if the json value is an JSON_TRUE or JSON_FALSE.
+	 *
+	 * @return true or false
+	 */
+	inline bool isBoolean() const noexcept
+	{
+		return json_is_boolean(m_handle.get());
+	}
 
 	/**
-	 * Write to the source the value.
+	 * Get the string value.
+	 *
+	 * @return the string
+	 */
+	inline std::string toString() const noexcept
+	{
+		auto value = json_string_value(m_handle.get());
+
+		return (value == nullptr) ? "" : value;
+	}
+
+	/**
+	 * Get the integer value.
+	 *
+	 * @return the value or 0
+	 */
+	inline int toInteger() const noexcept
+	{
+		return json_integer_value(m_handle.get());
+	}
+
+	/**
+	 * Get the real value.
+	 *
+	 * @return the value or 0
+	 */
+	inline double toReal() const noexcept
+	{
+		return json_real_value(m_handle.get());
+	}
+
+	/**
+	 * Convert to object.
+	 *
+	 * @return an object
+	 */
+	JsonObject toObject() const noexcept;
+
+	/**
+	 * Convert to array.
+	 *
+	 * @return an array
+	 */
+	JsonArray toArray() const noexcept;
+
+	/**
+	 * Write to a stream.
 	 *
-	 * @param value the value
+	 * @param out the out
+	 * @param flags the optional Jansson flags
+	 */
+	inline void write(std::ofstream &out, int flags = 0) const
+	{
+		auto content = dump(flags);
+
+		std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator<char>(out));
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @param out the out
+	 * @param flags the optional Jansson flags
+	 */
+	inline void write(std::ofstream &&out, int flags = 0) const
+	{
+		write(out, flags);
+	}
+
+	/**
+	 * Convert the Json value as a string.
+	 *
+	 * @return the string
+	 * @param flags the optional Jansson flags
 	 */
-	virtual void write(const JsonValue &value) = 0;
+	inline std::string dump(int flags = 0) const
+	{
+		auto str = json_dumps(m_handle.get(), flags);
+
+		if (str == nullptr)
+			return "";
+
+		std::string ret(str);
+		std::free(str);
+
+		return ret;
+	}
+
+	/**
+	 * Convert to native Jansson type.
+	 *
+	 * You should not call json_incref or json_decref on it as it is
+	 * automatically done.
+	 *
+	 * @return the json_t handle
+	 * @warning use this function with care
+	 */
+	inline operator json_t *() noexcept
+	{
+		return m_handle.get();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the json_t handle
+	 */
+	inline operator const json_t *() const noexcept
+	{
+		return m_handle.get();
+	}
+
+	/**
+	 * Equality operator.
+	 */
+	inline bool operator==(const JsonValue &other) const noexcept
+	{
+		return json_equal(m_handle.get(), other.m_handle.get());
+	}
 };
 
 /**
- * @class JsonReaderFile
- * @brief Read JSON data from a file
+ * @class JsonArray
+ * @brief Manipulate JSON arrays
  */
-class JsonReaderFile final : public JsonReader {
-private:
-	std::string m_path;
+class JsonArray final : public JsonValue {
+public:
+	/**
+	 * @class Ref
+	 * @brief Reference wrapper to be assignable
+	 */
+	class Ref final : public JsonValue {
+	private:
+		JsonArray &m_array;
+		int m_index;
+
+	public:
+		explicit inline Ref(JsonValue value, JsonArray &array, int index)
+			: JsonValue(std::move(value))
+			, m_array(array)
+			, m_index(index)
+		{
+		}
+
+		inline operator JsonValue() const noexcept
+		{
+			return *this;
+		}
+
+		inline JsonValue &operator*() noexcept
+		{
+			return *this;
+		}
+
+		inline JsonValue *operator->() noexcept
+		{
+			return this;
+		}
+
+		inline Ref &operator=(const JsonValue &value)
+		{
+			m_array.replace(value, m_index);
+
+			return *this;
+		}
+
+		inline Ref &operator=(JsonValue &&value)
+		{
+			m_array.replace(std::move(value), m_index);
+
+			return *this;
+		}
+	};
+
+	/**
+	 * @class Ptr
+	 * @brief Pointer wrapper for JsonValue iterators
+	 */
+	class Ptr final : public JsonValue {
+	public:
+		explicit Ptr(JsonValue value) noexcept
+			: JsonValue(std::move(value))
+		{
+		}
+
+		inline JsonValue &operator*() noexcept
+		{
+			return *this;
+		}
+
+		inline JsonValue *operator->() noexcept
+		{
+			return this;
+		}
+	};
+
+	class iterator final {
+	public:
+		using iterator_category = std::random_access_iterator_tag;
+		using difference_type = int;
+		using value_type = JsonValue;
+		using reference = Ref;
+		using pointer = Ptr;
+
+	private:
+		JsonArray &m_array;
+		int m_index;
+
+	public:
+		explicit inline iterator(JsonArray &array, int index = 0) noexcept
+			: m_array(array)
+			, m_index(index)
+		{
+		}
+
+		inline Ref operator*() const
+		{
+			return Ref(m_array.at(m_index), m_array, m_index);
+		}
+
+		inline Ptr operator->() const
+		{
+			return Ptr(m_array.at(m_index));
+		}
+
+		inline Ref operator[](int nindex) const noexcept
+		{
+			return Ref(m_array.at(m_index + nindex), m_array, m_index + nindex);
+		}
+
+		inline bool operator==(const iterator &other) const noexcept
+		{
+			return m_index == other.m_index;
+		}
+		
+		inline bool operator!=(const iterator &other) const noexcept
+		{
+			return m_index != other.m_index;
+		}
+
+		inline bool operator<(const iterator &other) const noexcept
+		{
+			return m_index < other.m_index;
+		}
+
+		inline bool operator<=(const iterator &other) const noexcept
+		{
+			return m_index <= other.m_index;
+		}
+
+		inline bool operator>(const iterator &other) const noexcept
+		{
+			return m_index > other.m_index;
+		}
+
+		inline bool operator>=(const iterator &other) const noexcept
+		{
+			return m_index >= other.m_index;
+		}
+
+		inline iterator &operator++() noexcept
+		{
+			++m_index;
+
+			return *this;
+		}
+
+		inline iterator operator++(int) noexcept
+		{
+			iterator save = *this;
+
+			++m_index;
+
+			return save;
+		}
+
+		inline iterator &operator--() noexcept
+		{
+			m_index--;
+
+			return *this;
+		}
+
+		inline iterator operator--(int) noexcept
+		{
+			iterator save = *this;
+
+			m_index--;
+
+			return save;
+		}
+
+		inline iterator &operator+=(int nindex) noexcept
+		{
+			m_index += nindex;
+
+			return *this;
+		}
+
+		inline iterator &operator-=(int nindex) noexcept
+		{
+			m_index -= nindex;
+
+			return *this;
+		}
+
+		inline iterator operator+(int nindex) const noexcept
+		{
+			return iterator(m_array, m_index + nindex);
+		}
+
+		inline iterator operator-(int nindex) const noexcept
+		{
+			return iterator(m_array, m_index - nindex);
+		}
+
+		inline int operator-(iterator other) const noexcept
+		{
+			return m_index - other.m_index;
+		}
+	};
+
+	class const_iterator final {
+	public:
+		using iterator_category = std::random_access_iterator_tag;
+		using difference_type = int;
+		using value_type = JsonValue;
+		using reference = JsonValue;
+		using pointer = Ptr;
+
+	private:
+		const JsonArray &m_array;
+		int m_index;
+
+	public:
+		explicit inline const_iterator(const JsonArray &array, int index = 0) noexcept
+			: m_array(array)
+			, m_index(index)
+		{
+		}
+
+		inline JsonValue operator*() const
+		{
+			return m_array.at(m_index);
+		}
+
+		inline Ptr operator->() const
+		{
+			return Ptr(m_array.at(m_index));
+		}
+
+		inline JsonValue operator[](int nindex) const noexcept
+		{
+			return m_array.at(m_index + nindex);
+		}
+
+		inline bool operator==(const const_iterator &other) const noexcept
+		{
+			return m_index == other.m_index;
+		}
+		
+		inline bool operator!=(const const_iterator &other) const noexcept
+		{
+			return m_index != other.m_index;
+		}
+
+		inline bool operator<(const const_iterator &other) const noexcept
+		{
+			return m_index < other.m_index;
+		}
+
+		inline bool operator<=(const const_iterator &other) const noexcept
+		{
+			return m_index <= other.m_index;
+		}
+
+		inline bool operator>(const const_iterator &other) const noexcept
+		{
+			return m_index > other.m_index;
+		}
+
+		inline bool operator>=(const const_iterator &other) const noexcept
+		{
+			return m_index >= other.m_index;
+		}
+
+		inline const_iterator &operator++() noexcept
+		{
+			++m_index;
+
+			return *this;
+		}
+
+		inline const_iterator operator++(int) noexcept
+		{
+			const_iterator save = *this;
+
+			++m_index;
+
+			return save;
+		}
+
+		inline const_iterator &operator--() noexcept
+		{
+			m_index--;
+
+			return *this;
+		}
+
+		inline const_iterator operator--(int) noexcept
+		{
+			const_iterator save = *this;
+
+			m_index--;
+
+			return save;
+		}
+
+		inline const_iterator &operator+=(int nindex) noexcept
+		{
+			m_index += nindex;
+
+			return *this;
+		}
+
+		inline const_iterator &operator-=(int nindex) noexcept
+		{
+			m_index -= nindex;
+
+			return *this;
+		}
+
+		inline const_iterator operator+(int nindex) const noexcept
+		{
+			return const_iterator(m_array, m_index + nindex);
+		}
+
+		inline const_iterator operator-(int nindex) const noexcept
+		{
+			return const_iterator(m_array, m_index - nindex);
+		}
+
+		inline int operator-(const_iterator other) const noexcept
+		{
+			return m_index - other.m_index;
+		}
+	};
+
+	using size_type = int;
+	using value_type = JsonValue;
+	using const_pointer = const value_type *;
+	using reference = JsonValue &;
+	using const_reference = const JsonValue &;
+	using difference_type = int;
+
+protected:
+	using JsonValue::JsonValue;
 
 public:
 	/**
-	 * Construct a JsonReader to a file. This function does not open
-	 * the file immediately.
+	 * Create an empty array.
+	 */
+	inline JsonArray()
+		: JsonValue(json_array())
+	{
+	}
+
+	/**
+	 * Create an array from a list of values.
+	 *
+	 * @param list the list
+	 */
+	inline JsonArray(std::initializer_list<value_type> list)
+		: JsonArray()
+	{
+		for (auto &v : list)
+			append(std::move(v));
+	}
+
+	/**
+	 * Returns an iterator to the beginning.
+	 *
+	 * @return the iterator
+	 */
+	inline iterator begin() noexcept
+	{
+		return iterator(*this, 0);
+	}
+
+	/**
+	 * Overloaded function.
 	 *
-	 * @param path the path
+	 * @return the iterator
+	 */
+	inline const_iterator begin() const noexcept
+	{
+		return const_iterator(*this, 0);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
 	 */
-	JsonReaderFile(std::string path);
+	inline const_iterator cbegin() const noexcept
+	{
+		return const_iterator(*this, 0);
+	}
+
+	/**
+	 * Returns an iterator to the end.
+	 *
+	 * @return the iterator
+	 */
+	inline iterator end() noexcept
+	{
+		return iterator(*this, size());
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline const_iterator end() const noexcept
+	{
+		return const_iterator(*this, size());
+	}
 
 	/**
-	 * Read the file and extract a JsonValue.
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline const_iterator cend() const noexcept
+	{
+		return const_iterator(*this, size());
+	}
+
+	/**
+	 * Get a value.
+	 *
+	 * @param index the index
+	 * @throw JsonError on error
+	 */
+	JsonValue at(int index) const;
+
+	/**
+	 * Get the number of values in the array
+	 *
+	 * @return the number or 0
+	 */
+	inline unsigned size() const noexcept
+	{
+		return json_array_size(m_handle.get());
+	}
+
+	/**
+	 * Insert the value at the beginning.
+	 *
+	 * @param value the value
+	 */
+	inline void push(const JsonValue &value) noexcept
+	{
+		json_array_insert(m_handle.get(), 0, value.m_handle.get());
+	}
+
+	/**
+	 * Insert a copy of the value at the end.
 	 *
-	 * @return a JsonValue (which will be JsonType::Object or JsonType::Array)
-	 * @throw JsonError on errors
+	 * @param value the value to insert
+	 */
+	inline void append(const JsonValue &value)
+	{
+		json_array_append(m_handle.get(), value.m_handle.get());
+	}
+
+	/**
+	 * Insert a copy of the value at the specified index.
+	 *
+	 * @param value the value to insert
+	 * @param index the position
 	 */
-	JsonValue read() override;
+	inline void insert(const JsonValue &value, int index)
+	{
+		json_array_insert(m_handle.get(), index, value.m_handle.get());
+	}
+
+	/**
+	 * Replace the value at the given index.
+	 *
+	 * @param value the value
+	 * @param index the index
+	 */
+	inline void replace(const JsonValue &value, int index)
+	{
+		json_array_set(m_handle.get(), index, value.m_handle.get());
+	}
+
+	/**
+	 * Get the value at the specified index.
+	 *
+	 * @param index the position
+	 * @return the value
+	 */
+	JsonValue operator[](int index) const noexcept;
+
+	/**
+	 * Access a value as a reference wrapper.
+	 *
+	 * @param index the position
+	 * @return the reference wrapper over the value
+	 */
+	Ref operator[](int index) noexcept;
 };
 
 /**
- * @class JsonWriterFile
- * @brief Write JSON data to file
+ * @class JsonObject
+ * @brief Object wrapper
  */
-class JsonWriterFile final : public JsonWriter {
-private:
-	std::string m_path;
+class JsonObject final : public JsonValue {
+public:
+	using key_type = std::string;
+	using mapped_type = JsonValue;
+	using size_type = int;
+	using value_type = std::pair<key_type, mapped_type>;
+	using const_pointer = const value_type *;
+	using reference = JsonValue &;
+	using const_reference = const JsonValue &;
+	using difference_type = int;
+
+	/**
+	 * @class Ref
+	 * @brief Wrapper for updating JsonObject
+	 *
+	 * This class is only used for the following functions:
+	 *
+	 *	JsonObject::operator[]
+	 */
+	class Ref final : public JsonValue {
+	private:
+		JsonObject &m_object;
+		std::string m_key;
+
+	public:
+		explicit inline Ref(JsonValue value, JsonObject &object, std::string key)
+			: JsonValue(std::move(value))
+			, m_object(object)
+			, m_key(std::move(key))
+		{
+		}
+
+		inline operator JsonValue() const noexcept
+		{
+			return *this;
+		}
+
+		inline JsonValue &operator*() noexcept
+		{
+			return *this;
+		}
+
+		inline JsonValue *operator->() noexcept
+		{
+			return this;
+		}
+
+		inline Ref &operator=(const JsonValue &value)
+		{
+			m_object.set(m_key, value);
+
+			return *this;
+		}
+
+		inline Ref &operator=(JsonValue &&value)
+		{
+			m_object.set(m_key, std::move(value));
+
+			return *this;
+		}
+	};
+
+	/**
+	 * @class Ptr
+	 * @brief Pointer wrapper for JsonValue iterators
+	 *
+	 * For const iterators, the real type is a JsonValue, for non const
+	 * iterators it's a ref so that user can edit it.
+	 */
+	template <typename Type>
+	class Ptr final {
+	private:
+		std::pair<std::string, Type> m_pair;
+
+	public:
+		inline Ptr(std::pair<std::string, Type> value) noexcept
+			: m_pair(std::move(value))
+		{
+		}
+
+		inline std::pair<std::string, Type> &operator*() noexcept
+		{
+			return m_pair;
+		}
+
+		inline std::pair<std::string, Type> *operator->() noexcept
+		{
+			return &m_pair;
+		}
+	};
+
+	/**
+	 * @class iterator
+	 * @brief Forward iterator
+	 */
+	class iterator {
+	public:
+		using value_type = std::pair<std::string, Ref>;
+
+	private:
+		JsonObject &m_object;
+		void *m_keyIt;
+
+		inline std::string key() const noexcept
+		{
+			return json_object_iter_key(m_keyIt);
+		}
+
+	public:
+		explicit inline iterator(JsonObject &object, void *keyIt) noexcept
+			: m_object(object)
+			, m_keyIt(keyIt)
+		{
+		}
+
+		inline value_type operator*() const
+		{
+			auto k = key();
+
+			return value_type(k, Ref(m_object[k], m_object, k));
+		}
+
+		inline Ptr<Ref> operator->() const
+		{
+			auto k = key();
+
+			return Ptr<Ref>({k, Ref(m_object[k], m_object, k)});
+		}
+
+		inline iterator &operator++() noexcept
+		{
+			m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt);
+
+			return *this;
+		}
+
+		inline iterator operator++(int) noexcept
+		{
+			iterator save = *this;
+
+			m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt);
+
+			return save;
+		}
+
+		inline operator==(iterator other) const noexcept
+		{
+			return m_keyIt == other.m_keyIt;
+		}
+
+		inline operator!=(iterator other) const noexcept
+		{
+			return m_keyIt != other.m_keyIt;
+		}
+	};
+
+	/**
+	 * @class const_iterator
+	 * @brief Forward iterator
+	 */
+	class const_iterator {
+	public:
+		using value_type = std::pair<std::string, JsonValue>;
+
+	private:
+		const JsonObject &m_object;
+		void *m_keyIt;
+
+		inline std::string key() const noexcept
+		{
+			return json_object_iter_key(m_keyIt);
+		}
+
+	public:
+		explicit inline const_iterator(const JsonObject &object, void *keyIt) noexcept
+			: m_object(object)
+			, m_keyIt(keyIt)
+		{
+		}
+
+		inline value_type operator*() const
+		{
+			auto k = key();
+
+			return value_type(k, m_object[k]);
+		}
+
+		inline Ptr<JsonValue> operator->() const
+		{
+			auto k = key();
+
+			return Ptr<JsonValue>({k, m_object[k]});
+		}
+
+		inline const_iterator &operator++() noexcept
+		{
+			m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt);
+
+			return *this;
+		}
+
+		inline const_iterator operator++(int) noexcept
+		{
+			const_iterator save = *this;
+
+			m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt);
+
+			return save;
+		}
+
+		inline operator==(const_iterator other) const noexcept
+		{
+			return m_keyIt == other.m_keyIt;
+		}
+
+		inline operator!=(const_iterator other) const noexcept
+		{
+			return m_keyIt != other.m_keyIt;
+		}
+	};
+
+protected:
+	using JsonValue::JsonValue;
 
 public:
 	/**
-	 * Construct a JsonReader to a file. This function does not open
-	 * the file immediately.
+	 * Create empty object.
+	 */
+	inline JsonObject()
+		: JsonValue(json_object())
+	{
+	}
+
+	/**
+	 * Create a JsonObject from an initializer_list.
+	 *
+	 * @param list the list of key-value pairs
+	 */
+	inline JsonObject(std::initializer_list<value_type> list)
+		: JsonObject()
+	{
+		for (auto &v : list) 
+			set(v.first, std::move(v.second));
+	}
+
+	/**
+	 * Returns an iterator to the beginning.
+	 *
+	 * @return the iterator
+	 */
+	inline iterator begin() noexcept
+	{
+		return iterator(*this, json_object_iter(m_handle.get()));
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline const_iterator begin() const noexcept
+	{
+		return const_iterator(*this, json_object_iter(m_handle.get()));
+	}
+
+	/**
+	 * Overloaded function.
 	 *
-	 * @param path
+	 * @return the iterator
+	 */
+	inline const_iterator cbegin() const noexcept
+	{
+		return const_iterator(*this, json_object_iter(m_handle.get()));
+	}
+
+	/**
+	 * Returns an iterator to the end.
+	 *
+	 * @return the iterator
+	 */
+	inline iterator end() noexcept
+	{
+		return iterator(*this, nullptr);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
 	 */
-	JsonWriterFile(std::string path);
+	inline const_iterator end() const noexcept
+	{
+		return const_iterator(*this, nullptr);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline const_iterator cend() const noexcept
+	{
+		return const_iterator(*this, nullptr);
+	}
+
+	/**
+	 * Check if the object contains a specific property.
+	 *
+	 * @param key the key
+	 * @return true if the object contains the key
+	 */
+	inline bool contains(const std::string &key) const noexcept
+	{
+		return json_object_get(m_handle.get(), key.c_str()) != nullptr;
+	}
+
+	/**
+	 * Get the number of items in the object.
+	 *
+	 * @return the number of items
+	 */
+	inline unsigned size() const noexcept
+	{
+		return json_object_size(m_handle.get());
+	}
 
 	/**
-	 * Write to the file.
+	 * Set the value as key in the object.
+	 *
+	 * @param key the key
+	 * @param value the value
+	 */
+	inline void set(const std::string &key, const JsonValue &value) noexcept
+	{
+		json_object_set(m_handle.get(), key.c_str(), value.m_handle.get());
+	}
+
+	/**
+	 * Access an object as a wrapper so that you can update its content
+	 * with convenience.
+	 *
+	 * @param key the key
+	 */
+	Ref operator[](const std::string &key);
+
+	/**
+	 * Get the value at the specified key. If the value is not found, an
+	 * empty value is returned
 	 *
-	 * @param value the value
+	 * @param key the key
+	 * @return the value
+	 */
+	JsonValue operator[](const std::string &key) const;
+};
+
+/**
+ * @class JsonDocument
+ * @brief Read files and strings to create Json values
+ */
+class JsonDocument final {
+private:
+	JsonValue m_value;
+
+	JsonValue read(std::string, int flags) const;
+	JsonValue read(std::ifstream &stream, int flags) const;
+
+public:
+	/**
+	 * Construct a document from a file.
+	 *
+	 * @param stream the stream
+	 * @param flags the optional Jansson flags
 	 * @throw JsonError on errors
 	 */
-	void write(const JsonValue &value) override;
+	JsonDocument(std::ifstream &fstream, int flags = 0);
+
+	/**
+	 * Construct a document from a file.
+	 *
+	 * @param stream the stream
+	 * @param flags the optional Jansson flags
+	 * @throw JsonError on errors
+	 */
+	JsonDocument(std::ifstream &&stream, int flags = 0);
+
+	/**
+	 * Construct a document from a file.
+	 *
+	 * @param stream the stream
+	 * @param flags the optional Jansson flags
+	 * @throw JsonError on errors
+	 */
+	JsonDocument(std::string content, int flags = 0);
+
+	/**
+	 * Check if the document contains an object.
+	 *
+	 * @return true if object
+	 */
+	inline bool isObject() const noexcept
+	{
+		return m_value.isObject();
+	}
+
+	/**
+	 * Check if the document contains an array.
+	 *
+	 * @return true if array
+	 */
+	inline bool isArray() const noexcept
+	{
+		return m_value.isArray();
+	}
+
+	/**
+	 * Convert the document value to object
+	 *
+	 * @return the value as object
+	 */
+	inline JsonObject toObject() const noexcept
+	{
+		return m_value.toObject();
+	}
+
+	/**
+	 * Convert the document value to array
+	 *
+	 * @return the value as array
+	 */
+	inline JsonArray toArray() const noexcept
+	{
+		return m_value.toArray();
+	}
 };
 
 #endif // !_JSON_H_
--- a/C++/Tests/Json/main.cpp	Wed Feb 25 12:42:06 2015 +0100
+++ b/C++/Tests/Json/main.cpp	Wed Feb 25 13:53:41 2015 +0100
@@ -16,6 +16,9 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <set>
+#include <unordered_map>
+
 #include <gtest/gtest.h>
 
 #include "Json.h"
@@ -125,159 +128,9 @@
 }
 
 /* --------------------------------------------------------
- * I/O
- * -------------------------------------------------------- */
-
-TEST(Open, simple)
-{
-	try {
-		JsonReaderFile reader{SOURCE "/data/simple.json"};
-
-		(void)reader.read();
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-TEST(Write, simpleArray)
-{
-	try {
-		// Write
-		{
-			JsonArray array;
-
-			array.append(123);
-			array.append(true);
-
-			JsonWriterFile{BINARY "/write-array.json"}.write(array);
-		}
-
-		// Read
-		{
-			JsonArray array = JsonReaderFile{BINARY "/write-array.json"}.read().toArray();
-
-			ASSERT_TRUE(array.isArray());
-			ASSERT_EQ(123, array[0].toInteger());
-			ASSERT_TRUE(array[1].isTrue());
-		}
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-TEST(Write, simpleObject)
-{
-	try {
-		// Write
-		{
-			JsonObject object;
-
-			object.set("integer", 123);
-			object.set("true", true);
-
-			JsonWriterFile{BINARY "/write-object.json"}.write(object);
-		}
-
-		// Read
-		{
-			JsonObject object = JsonReaderFile{BINARY "/write-object.json"}.read().toObject();
-
-			ASSERT_TRUE(object.isObject());
-			ASSERT_EQ(123, object["integer"].toInteger());
-			ASSERT_TRUE(object["true"]);
-		}
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-/* --------------------------------------------------------
  * Object
  * -------------------------------------------------------- */
 
-TEST(Object, simple)
-{
-	try {
-		JsonValue value = JsonReaderFile{SOURCE "/data/object.json"}.read();
-
-		ASSERT_TRUE(value.isObject());
-		ASSERT_FALSE(value.isArray());
-
-		JsonObject object = value.toObject();
-
-		ASSERT_TRUE(object.isObject());
-
-		JsonValue name = object["name"];
-		JsonValue description = object["description"];
-
-		ASSERT_TRUE(name.isString());
-		ASSERT_TRUE(description.isString());
-		ASSERT_EQ("simple", name.toString());
-		ASSERT_EQ("basic JSON file", description.toString());
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-TEST(Object, all)
-{
-	try {
-		JsonObject object = JsonReaderFile{SOURCE "/data/object-all.json"}.read().toObject();
-
-		ASSERT_TRUE(object["integer"].isInteger());
-		ASSERT_TRUE(object["integer"].isNumber());
-		ASSERT_EQ(123, object["integer"].toInteger());
-
-		ASSERT_TRUE(object["real"].isReal());
-		ASSERT_TRUE(object["real"].isNumber());
-		ASSERT_EQ(9.2, object["real"].toReal());
-
-		ASSERT_TRUE(object["false"].isBoolean());
-		ASSERT_TRUE(object["false"].isFalse());
-		ASSERT_FALSE(object["false"].isTrue());
-
-		ASSERT_TRUE(object["true"].isBoolean());
-		ASSERT_TRUE(object["true"].isTrue());
-		ASSERT_FALSE(object["true"].isFalse());
-
-		ASSERT_TRUE(object["null"].isNull());
-
-		ASSERT_TRUE(object["object"].isObject());
-		ASSERT_TRUE(object["array"].isArray());
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-TEST(Object, forAll)
-{
-	try {
-		JsonObject object = JsonReaderFile{SOURCE "/data/object-all.json"}.read().toObject();
-
-		object.forAll([] (const auto &key, const auto &value) {
-			if (key == "integer") {
-				ASSERT_TRUE(value.isInteger());
-				ASSERT_EQ(123, value.toInteger());
-			} else if (key == "real") {
-				ASSERT_TRUE(value.isReal());
-				ASSERT_EQ(9.2, value.toReal());
-			} else if (key == "false") {
-				ASSERT_TRUE(value.isFalse());
-			} else if (key == "true") {
-				ASSERT_TRUE(value.isTrue());
-			} else if (key == "null") {
-				ASSERT_TRUE(value.isNull());
-			} else if (key == "object") {
-				ASSERT_TRUE(value.isObject());
-			} else if (key == "array") {
-				ASSERT_TRUE(value.isArray());
-			}
-		});
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
-
 TEST(Object, set)
 {
 	try {
@@ -295,89 +148,54 @@
 	}
 }
 
-/* --------------------------------------------------------
- * Array
- * -------------------------------------------------------- */
-
-TEST(Array, simple)
+TEST(ObjectInitializer, simple)
 {
 	try {
-		JsonValue value = JsonReaderFile{SOURCE "/data/array.json"}.read();
-
-		ASSERT_TRUE(value.isArray());
-		ASSERT_FALSE(value.isObject());
-
-		JsonArray array = value.toArray();
+		JsonObject object{
+			{ "username", "jean" },
+			{ "age", 99 }
+		};
 
-		ASSERT_TRUE(array.isArray());
-
-		ASSERT_EQ(3, array.size());
-		ASSERT_EQ(1, array[0].toInteger());
-		ASSERT_EQ(2, array[1].toInteger());
-		ASSERT_EQ(3, array[2].toInteger());
-	} catch (const JsonError &ex) {
+		ASSERT_EQ(2, static_cast<int>(object.size()));
+		ASSERT_EQ("jean", object["username"].toString());
+		ASSERT_EQ(99, object["age"].toInteger());
+	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
 }
 
-TEST(Array, all)
+TEST(ObjectInitializer, deep)
 {
 	try {
-		JsonArray array = JsonReaderFile{SOURCE "/data/array-all.json"}.read().toArray();
-
-		ASSERT_TRUE(array[0].isInteger());
-		ASSERT_TRUE(array[0].isNumber());
-		ASSERT_EQ(123, array[0].toInteger());
-
-		ASSERT_TRUE(array[1].isReal());
-		ASSERT_TRUE(array[1].isNumber());
-		ASSERT_EQ(9.2, array[1].toReal());
+		JsonObject object{
+			{ "username", "jean"		},
+			{ "age", 99			},
+			{ "network", JsonObject{
+					{ "port", 9999 		},
+					{ "host", "localhost"	}
+				}
+			}
+		};
 
-		ASSERT_TRUE(array[2].isBoolean());
-		ASSERT_TRUE(array[2].isFalse());
-		ASSERT_FALSE(array[2].isTrue());
+		// First
+		ASSERT_EQ(3, static_cast<int>(object.size()));
+		ASSERT_EQ("jean", object["username"].toString());
+		ASSERT_EQ(99, object["age"].toInteger());
 
-		ASSERT_TRUE(array[3].isBoolean());
-		ASSERT_TRUE(array[3].isTrue());
-		ASSERT_FALSE(array[3].isFalse());
-
-		ASSERT_TRUE(array[4].isNull());
-
-		ASSERT_TRUE(array[5].isObject());
-		ASSERT_TRUE(array[6].isArray());
-	} catch (const JsonError &ex) {
+		// Second
+		JsonObject network = object["network"].toObject();
+		ASSERT_TRUE(network.isObject());
+		ASSERT_EQ(2, static_cast<int>(network.size()));
+		ASSERT_EQ(9999, network["port"].toInteger());
+		ASSERT_EQ("localhost", network["host"].toString());
+	} catch (const std::exception &ex) {
 		FAIL() << ex.what();
 	}
 }
 
-TEST(Array, forAll)
-{
-	try {
-		JsonArray array = JsonReaderFile{SOURCE "/data/array-all.json"}.read().toArray();
-
-		array.forAll([] (auto index, const auto &value) {
-			if (index == 0) {
-				ASSERT_TRUE(value.isInteger());
-				ASSERT_EQ(123, value.toInteger());
-			} else if (index == 1) {
-				ASSERT_TRUE(value.isReal());
-				ASSERT_EQ(9.2, value.toReal());
-			} else if (index == 2) {
-				ASSERT_TRUE(value.isFalse());
-			} else if (index == 3) {
-				ASSERT_TRUE(value.isTrue());
-			} else if (index == 4) {
-				ASSERT_TRUE(value.isNull());
-			} else if (index == 5) {
-				ASSERT_TRUE(value.isObject());
-			} else if (index == 6) {
-				ASSERT_TRUE(value.isArray());
-			}
-		});
-	} catch (const JsonError &ex) {
-		FAIL() << ex.what();
-	}
-}
+/* --------------------------------------------------------
+ * Array
+ * -------------------------------------------------------- */
 
 TEST(Array, push)
 {
@@ -390,7 +208,7 @@
 		array.push("hello");
 		array.push(true);
 
-		ASSERT_EQ(3, array.size());
+		ASSERT_EQ(3, static_cast<int>(array.size()));
 		ASSERT_TRUE(array[0].isTrue());
 		ASSERT_EQ("hello", array[1].toString());
 		ASSERT_EQ(1, array[2].toInteger());
@@ -410,7 +228,7 @@
 		array.append("hello");
 		array.append(true);
 
-		ASSERT_EQ(3, array.size());
+		ASSERT_EQ(3, static_cast<int>(array.size()));
 		ASSERT_EQ(1, array[0].toInteger());
 		ASSERT_EQ("hello", array[1].toString());
 		ASSERT_TRUE(array[2].isTrue());
@@ -430,7 +248,7 @@
 		array.insert("hello", 1);
 		array.insert(true, 0);
 
-		ASSERT_EQ(3, array.size());
+		ASSERT_EQ(3, static_cast<int>(array.size()));
 		ASSERT_TRUE(array[0].isTrue());
 		ASSERT_EQ(1, array[1].toInteger());
 		ASSERT_EQ("hello", array[2].toString());
@@ -439,6 +257,471 @@
 	}
 }
 
+TEST(ArrayInitializer, simple)
+{
+	try {
+		JsonArray array{123, true, "hello"};
+
+		ASSERT_EQ(3, static_cast<int>(array.size()));
+		ASSERT_EQ(123, array[0].toInteger());
+		ASSERT_TRUE(array[1].isTrue());
+		ASSERT_EQ("hello", array[2].toString());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(ArrayInitializer, deep)
+{
+	try {
+		JsonArray array{
+			123,
+			true,
+			"hello",
+			JsonArray{
+				321,
+				false,
+				"olleh"
+			}
+		};
+
+		// First
+		ASSERT_EQ(4, static_cast<int>(array.size()));
+		ASSERT_EQ(123, array[0].toInteger());
+		ASSERT_TRUE(array[1].isTrue());
+		ASSERT_EQ("hello", array[2].toString());
+
+		// Second
+		JsonArray array2 = array[3].toArray();
+		ASSERT_TRUE(array.isArray());
+		ASSERT_EQ(3, static_cast<int>(array2.size()));
+		ASSERT_EQ(321, array2[0].toInteger());
+		ASSERT_TRUE(array2[1].isFalse());
+		ASSERT_EQ("olleh", array2[2].toString());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+/* --------------------------------------------------------
+ * I/O
+ * -------------------------------------------------------- */
+
+TEST(FileRead, simple)
+{
+	try {
+		JsonDocument doc(std::ifstream(SOURCE "/data/simple.json"));
+
+		ASSERT_TRUE(doc.isObject());
+		ASSERT_FALSE(doc.isArray());
+
+		JsonObject object = doc.toObject();
+		JsonArray array = doc.toArray();
+
+		ASSERT_TRUE(object.isObject());
+		ASSERT_FALSE(array.isArray());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(FileRead, fail)
+{
+	try {
+		JsonDocument(std::ifstream(SOURCE "/data/notexist.json"));
+
+		FAIL() << "Exception expected";
+	} catch (const JsonError &) {
+	}
+}
+
+TEST(FileWrite, simple)
+{
+	try {
+		JsonObject object{
+			{ "name", "jean" },
+			{ "age", 99 }
+		};
+
+		object.write(std::ofstream(BINARY "/object-write.json"));
+
+		JsonObject object2 = JsonDocument(std::ifstream(BINARY "/object-write.json")).toObject();
+
+		ASSERT_EQ(object2, object);
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(StringRead, simple)
+{
+	try {
+		JsonDocument doc("{ \"license\": \"ISC\" }");
+
+		ASSERT_TRUE(doc.isObject());
+		ASSERT_FALSE(doc.isArray());
+
+		JsonObject object = doc.toObject();
+		JsonArray array = doc.toArray();
+
+		ASSERT_TRUE(object.isObject());
+		ASSERT_FALSE(array.isArray());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(StringRead, fail)
+{
+	try {
+		JsonDocument("{ \"license\": ISC }");
+
+		FAIL() << "Exception expected";
+	} catch (const JsonError &ex) {
+	}
+}
+
+TEST(StringWrite, simple)
+{
+	try {
+		JsonObject object{
+			{ "name", "jean" },
+			{ "age", 99 }
+		};
+
+		JsonObject object2 = JsonDocument(object.dump()).toObject();
+
+		ASSERT_EQ(object2, object);
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+/* --------------------------------------------------------
+ * Object read
+ * -------------------------------------------------------- */
+
+class ObjectRead : public testing::Test {
+protected:
+	JsonObject m_object;
+	JsonObject m_objectAll;
+
+public:
+	ObjectRead()
+	{
+		m_object = JsonDocument(std::ifstream(SOURCE "/data/object.json")).toObject();
+		m_objectAll = JsonDocument(std::ifstream(SOURCE "/data/object-all.json")).toObject();
+	}
+};
+
+TEST_F(ObjectRead, simple)
+{
+	try {
+		JsonValue name = m_object["name"];
+		JsonValue description = m_object["description"];
+
+		ASSERT_TRUE(name.isString());
+		ASSERT_TRUE(description.isString());
+		ASSERT_EQ("simple", name.toString());
+		ASSERT_EQ("basic JSON file", description.toString());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST_F(ObjectRead, all)
+{
+	try {
+		ASSERT_TRUE(m_objectAll.contains("integer"));
+		ASSERT_FALSE(m_objectAll.contains("unexistant"));
+
+		ASSERT_TRUE(m_objectAll["integer"].isInteger());
+		ASSERT_TRUE(m_objectAll["integer"].isNumber());
+		ASSERT_EQ(123, m_objectAll["integer"].toInteger());
+
+		ASSERT_TRUE(m_objectAll["real"].isReal());
+		ASSERT_TRUE(m_objectAll["real"].isNumber());
+		ASSERT_EQ(9.2, m_objectAll["real"].toReal());
+
+		ASSERT_TRUE(m_objectAll["false"].isBoolean());
+		ASSERT_TRUE(m_objectAll["false"].isFalse());
+		ASSERT_FALSE(m_objectAll["false"].isTrue());
+
+		ASSERT_TRUE(m_objectAll["true"].isBoolean());
+		ASSERT_TRUE(m_objectAll["true"].isTrue());
+		ASSERT_FALSE(m_objectAll["true"].isFalse());
+
+		ASSERT_TRUE(m_objectAll["null"].isNull());
+
+		ASSERT_TRUE(m_objectAll["object"].isObject());
+		ASSERT_TRUE(m_objectAll["array"].isArray());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+/* --------------------------------------------------------
+ * Array read
+ * -------------------------------------------------------- */
+
+class ArrayRead : public testing::Test {
+protected:
+	JsonArray m_array;
+	JsonArray m_arrayAll;
+
+public:
+	ArrayRead()
+	{
+		m_array = JsonDocument(std::ifstream(SOURCE "/data/array.json")).toArray();
+		m_arrayAll = JsonDocument(std::ifstream(SOURCE "/data/array-all.json")).toArray();
+	}
+};
+
+TEST_F(ArrayRead, simple)
+{
+	try {
+		ASSERT_EQ(3, static_cast<int>(m_array.size()));
+		ASSERT_EQ(1, m_array[0].toInteger());
+		ASSERT_EQ(2, m_array[1].toInteger());
+		ASSERT_EQ(3, m_array[2].toInteger());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST_F(ArrayRead, all)
+{
+	try {
+		ASSERT_TRUE(m_arrayAll[0].isInteger());
+		ASSERT_TRUE(m_arrayAll[0].isNumber());
+		ASSERT_EQ(123, m_arrayAll[0].toInteger());
+
+		ASSERT_TRUE(m_arrayAll[1].isReal());
+		ASSERT_TRUE(m_arrayAll[1].isNumber());
+		ASSERT_EQ(9.2, m_arrayAll[1].toReal());
+
+		ASSERT_TRUE(m_arrayAll[2].isBoolean());
+		ASSERT_TRUE(m_arrayAll[2].isFalse());
+		ASSERT_FALSE(m_arrayAll[2].isTrue());
+
+		ASSERT_TRUE(m_arrayAll[3].isBoolean());
+		ASSERT_TRUE(m_arrayAll[3].isTrue());
+		ASSERT_FALSE(m_arrayAll[3].isFalse());
+
+		ASSERT_TRUE(m_arrayAll[4].isNull());
+
+		ASSERT_TRUE(m_arrayAll[5].isObject());
+		ASSERT_TRUE(m_arrayAll[6].isArray());
+	} catch (const JsonError &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+/* --------------------------------------------------------
+ * Object iterators
+ * -------------------------------------------------------- */
+
+class ObjectIteratorsTest : public testing::Test {
+protected:
+	JsonObject m_object;
+
+public:
+	ObjectIteratorsTest()
+	{
+		m_object.set("integer", 1);
+		m_object.set("string", "hello");
+		m_object.set("boolean", true);
+	}
+};
+
+TEST_F(ObjectIteratorsTest, operators)
+{
+	// Read only (non const)
+	{
+		std::set<std::string> expected{"boolean", "integer", "string"};
+		std::set<std::string> result;
+		std::unordered_map<std::string, JsonValue> values;
+		JsonObject::iterator it = m_object.begin();
+		JsonObject::iterator end = m_object.end();
+
+		while (it != end) {
+			values.insert({it->first, it->second});
+			result.insert((*it++).first);
+		}
+
+		ASSERT_EQ(expected, result);
+		ASSERT_EQ(1, values["integer"].toInteger());
+		ASSERT_EQ("hello", values["string"].toString());
+		ASSERT_TRUE(values["boolean"].isTrue());
+	}
+
+	// Read only (const)
+	{
+		std::set<std::string> expected{"boolean", "integer", "string"};
+		std::set<std::string> result;
+		std::unordered_map<std::string, JsonValue> values;
+		JsonObject::const_iterator it = m_object.cbegin();
+		JsonObject::const_iterator end = m_object.cend();
+
+		while (it != end) {
+			values.insert({it->first, it->second});
+			result.insert((*it++).first);
+		}
+
+		ASSERT_EQ(expected, result);
+		ASSERT_EQ(1, values["integer"].toInteger());
+		ASSERT_EQ("hello", values["string"].toString());
+		ASSERT_TRUE(values["boolean"].isTrue());
+	}
+}
+
+TEST_F(ObjectIteratorsTest, assign)
+{
+	// Assign (non const)
+	{
+		JsonObject::iterator it = m_object.begin();
+		std::string key = it->first;
+
+		it->second = JsonValue("CHANGED");
+
+		ASSERT_EQ("CHANGED", m_object[key].toString());
+		ASSERT_EQ("CHANGED", it->second.toString());
+		ASSERT_EQ(3, static_cast<int>(m_object.size()));
+	}
+}
+
+TEST_F(ObjectIteratorsTest, assignConst)
+{
+	// Assign (const)
+	{
+		JsonObject::const_iterator it = m_object.cbegin();
+		std::string key = it->first;
+		JsonValue orig = it->second;
+
+		it->second = JsonValue("CHANGED");
+
+		ASSERT_TRUE(m_object.contains(key));
+		ASSERT_EQ(orig, m_object[key]);
+		ASSERT_EQ(3, static_cast<int>(m_object.size()));
+	}
+}
+
+/* --------------------------------------------------------
+ * Array iterators
+ * -------------------------------------------------------- */
+
+class ArrayIteratorsTest : public testing::Test {
+protected:
+	JsonArray m_array;
+
+public:
+	ArrayIteratorsTest()
+	{
+		m_array.append(1);
+		m_array.append("hello");
+		m_array.append(true);
+	}
+};
+
+TEST_F(ArrayIteratorsTest, operators)
+{
+	// Read only (non const)
+	{
+		JsonArray::iterator it = m_array.begin();
+
+		ASSERT_EQ(1, (*it).toInteger());
+		ASSERT_EQ(1, it->toInteger());
+		ASSERT_EQ("hello", it[1].toString());
+		ASSERT_TRUE(it[2].isTrue());
+
+		JsonArray::iterator it2 = it + 1;
+		ASSERT_EQ(1, it2[-1].toInteger());
+		ASSERT_EQ("hello", it2->toString());
+		ASSERT_TRUE(it2[1].isTrue());
+
+		JsonArray::iterator it3 = it;
+		ASSERT_TRUE(it2 != it);
+		ASSERT_FALSE(it3 != it);
+
+		ASSERT_FALSE(it2 == it);
+		ASSERT_TRUE(it3 == it);
+
+		ASSERT_TRUE(it3 >= it);
+		ASSERT_TRUE(it2 >= it);
+
+		ASSERT_TRUE(it2 > it);
+		ASSERT_FALSE(it3 > it);
+
+		ASSERT_FALSE(it2 <= it);
+		ASSERT_TRUE(it3 <= it);
+
+		ASSERT_FALSE(it2 < it);
+		ASSERT_FALSE(it3 < it);
+	}
+
+	// Read only (const)
+	{
+		JsonArray::const_iterator it = m_array.cbegin();
+
+		ASSERT_EQ(1, (*it).toInteger());
+		ASSERT_EQ(1, it->toInteger());
+		ASSERT_EQ("hello", it[1].toString());
+		ASSERT_TRUE(it[2].isTrue());
+
+		JsonArray::const_iterator it2 = it + 1;
+		ASSERT_EQ(1, it2[-1].toInteger());
+		ASSERT_EQ("hello", it2->toString());
+		ASSERT_TRUE(it2[1].isTrue());
+
+		JsonArray::const_iterator it3 = it;
+		ASSERT_TRUE(it2 != it);
+		ASSERT_FALSE(it3 != it);
+
+		ASSERT_FALSE(it2 == it);
+		ASSERT_TRUE(it3 == it);
+
+		ASSERT_TRUE(it3 >= it);
+		ASSERT_TRUE(it2 >= it);
+
+		ASSERT_TRUE(it2 > it);
+		ASSERT_FALSE(it3 > it);
+
+		ASSERT_FALSE(it2 <= it);
+		ASSERT_TRUE(it3 <= it);
+
+		ASSERT_FALSE(it2 < it);
+		ASSERT_FALSE(it3 < it);
+	}
+}
+
+TEST_F(ArrayIteratorsTest, assign)
+{
+	// Assign (non const)
+	{
+		JsonArray::iterator it = m_array.begin();
+
+		*it = JsonValue(9999);
+
+		ASSERT_EQ(3, static_cast<int>(m_array.size()));
+		ASSERT_EQ(9999, it->toInteger());
+		ASSERT_EQ(9999, m_array[0].toInteger());
+	}
+}
+
+TEST_F(ArrayIteratorsTest, assignConst)
+{
+	// Assign (const)
+	{
+		JsonArray::const_iterator it = m_array.cbegin();
+
+		*it = JsonValue(9999);
+
+		ASSERT_EQ(3, static_cast<int>(m_array.size()));
+		ASSERT_EQ(1, it->toInteger());
+		ASSERT_EQ(1, m_array[0].toInteger());
+	}
+}
+
 int main(int argc, char **argv)
 {
 	testing::InitGoogleTest(&argc, argv);