changeset 405:f81478065901

Json: - Complete rewrite, - Convert jansson tree to own C++ tree, - Conversion from Value to Object/Array makes copies
author David Demelier <markand@malikania.fr>
date Mon, 05 Oct 2015 17:04:40 +0200
parents 9ab878fb9fa2
children d485d36a8de1
files C++/modules/Json/Json.cpp C++/modules/Json/Json.h
diffstat 2 files changed, 570 insertions(+), 1425 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Json/Json.cpp	Mon Oct 05 14:39:57 2015 +0200
+++ b/C++/modules/Json/Json.cpp	Mon Oct 05 17:04:40 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Json.cpp -- jansson C++11 wrapper
+ * Json.cpp -- C++14 JSON manipulation using jansson parser
  *
  * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
  *
@@ -16,147 +16,147 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <stdexcept>
+#include "Json.h"
 
-#include "Json.h"
+#include <jansson.h>
 
 namespace json {
 
-/* --------------------------------------------------------
- * Object
- * -------------------------------------------------------- */
+namespace {
+
+void readObject(Value &parent, json_t *object);
+void readArray(Value &parent, json_t *array);
+
+Value readValue(json_t *v)
+{
+	if (json_is_null(v)) {
+		return Value{nullptr};
+	}
+	if (json_is_string(v)) {
+		return Value{json_string_value(v)};
+	}
+	if (json_is_number(v)) {
+		return Value{json_number_value(v)};
+	}
+	if (json_is_boolean(v)) {
+		return Value{json_boolean_value(v)};
+	}
+	if (json_is_object(v)) {
+		Object object;
+
+		readObject(object, v);
+
+		return object;
+	}
+	if (json_is_array(v)) {
+		Array array;
+
+		readArray(array, v);
+
+		return array;
+	}
+
+	return Value{};
+}
+
+void readObject(Value &parent, json_t *object)
+{
+	const char *key;
+	json_t *value;
+
+	json_object_foreach(object, key, value) {
+		static_cast<Object &>(parent).insert(key, readValue(value));
+	}
+}
+
+void readArray(Value &parent, json_t *array)
+{
+	size_t index;
+	json_t *value;
+
+	json_array_foreach(array, index, value) {
+		static_cast<Array &>(parent).append(readValue(value));
+	}
+}
+
+template <typename Func, typename... Args>
+Value convert(Func fn, Args&&... args)
+{
+	json_error_t error;
+	json_t *json = fn(std::forward<Args>(args)..., &error);
+
+	if (json == nullptr) {
+		throw Error{error.text, error.source, error.line, error.column, error.position};
+	}
+
+	Value value;
+
+	if (json_is_object(json)) {
+		value = Object{};
+		readObject(value, json);
+	} else {
+		value = Array{};
+		readArray(value, json);
+	}
+
+	json_decref(json);
+
+	return value;
+}
+
+} // !namespace
+
+bool Value::toBool() const noexcept
+{
+	if (m_type != Type::Boolean)
+		return false;
+
+	return m_boolean;
+}
+
+double Value::toNumber() const noexcept
+{
+	if (m_type != Type::Number)
+		return 0;
+
+	return m_number;
+}
+
+std::string Value::toString() const noexcept
+{
+	if (m_type != Type::String) {
+		return "";
+	}
+
+	return m_string;
+}
 
 Object Value::toObject() const noexcept
 {
-	json_incref(m_handle.get());
+	if (m_type != Type::Object) {
+		return Object{};
+	}
 
-	return Object(m_handle.get());
+	return Object(*this);
 }
 
 Array Value::toArray() const noexcept
 {
-	json_incref(m_handle.get());
-
-	return Array(m_handle.get());
-}
-
-/* --------------------------------------------------------
- * Array
- * -------------------------------------------------------- */
-
-Value Array::at(int index) const
-{
-	auto value = json_array_get(m_handle.get(), index);
-
-	if (value == nullptr)
-		throw Error("index out of bounds");
-
-	json_incref(value);
-
-	return Value{value};
-}
-
-Value Array::operator[](int index) const noexcept
-{
-	auto value = json_array_get(m_handle.get(), index);
-
-	if (value == nullptr)
-		return Value();
-
-	json_incref(value);
+	if (m_type != Type::Array) {
+		return Array{};
+	}
 
-	return Value(value);
-}
-
-Array::Ref Array::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);
-}
-
-/* --------------------------------------------------------
- * Object
- * -------------------------------------------------------- */
-
-Object::Ref Object::operator[](const std::string &name)
-{
-	if (typeOf() != Type::Object)
-		return Ref(Value(), *this, name);
-
-	auto value = json_object_get(m_handle.get(), name.c_str());
-
-	json_incref(value);
-
-	return Ref(value, *this, name);
+	return Array(*this);
 }
 
-Value Object::operator[](const std::string &name) const
+Document::Document(Buffer buffer)
 {
-	if (typeOf() != Type::Object)
-		return Value();
-
-	auto value = json_object_get(m_handle.get(), name.c_str());
-
-	if (value == nullptr)
-		return Value();
-
-	json_incref(value);
-
-	return Value(value);
-}
-
-/* --------------------------------------------------------
- * Document
- * -------------------------------------------------------- */
-
-Value Document::read(std::string content, int flags) const
-{
-	json_error_t error;
-	json_t *json = json_loads(content.c_str(), flags, &error);
-
-	if (json == nullptr)
-		throw Error(error);
-
-	return Value(json);
+	m_value = convert(json_loads, buffer.text.c_str(), 0);
 }
 
-Value Document::read(std::ifstream &stream, int flags) const
+Document::Document(File file)
 {
-	if (!stream.is_open())
-		throw Error("File not opened");
-
-	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);
-}
-
-Document::Document(std::ifstream &stream, int flags)
-{
-	m_value = read(stream, flags);
-}
-
-Document::Document(std::ifstream &&stream, int flags)
-{
-	m_value = read(stream, flags);
-}
-
-Document::Document(std::string content, int flags)
-{
-	m_value = read(std::move(content), flags);
+	m_value = convert(json_load_file, file.path.c_str(), 0);
 }
 
 } // !json
\ No newline at end of file
--- a/C++/modules/Json/Json.h	Mon Oct 05 14:39:57 2015 +0200
+++ b/C++/modules/Json/Json.h	Mon Oct 05 17:04:40 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Json.h -- jansson C++11 wrapper
+ * Json.h -- C++14 JSON manipulation using jansson parser
  *
  * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
  *
@@ -19,1589 +19,734 @@
 #ifndef _JSON_H_
 #define _JSON_H_
 
-#include <algorithm>
-#include <cerrno>
-#include <cstdlib>
-#include <cstring>
-#include <initializer_list>
-#include <fstream>
-#include <iterator>
-#include <memory>
+/**
+ * @file Json.h
+ * @brief Jansson C++14 wrapper
+ *
+ * These classes can be used to build or parse JSON documents using jansson library. It is designed to be safe
+ * and explicit. It does not implement implicit sharing like jansson so when you access (e.g. Value::toObject) values
+ * you get real copies, thus when you read big documents it can has a performance cost.
+ */
+
+#include <map>
 #include <string>
 #include <utility>
-
-#include <jansson.h>
-
-/**
- * @file Json.h
- * @brief A jansson C++ modern wrapper
- *
- * Because of the Jansson implementation, all these classes are implicitly
- * shared.
- *
- * This means that you can't set any value to an existing value as it would
- * change a value which may be used somewhere else, instead you must set
- * or replace elements in Object and Array respectively.
- *
- * However, copy constructors are implemented as deep copy so take care of
- * not copying values mistakenly.
- */
+#include <vector>
 
 namespace json {
 
 /**
- * @class Type
- * @brief Json value type
+ * @enum Type
+ * @brief Type of Value.
  */
 enum class Type {
-	Object = JSON_OBJECT,		//!< Object
-	Array = JSON_ARRAY,		//!< Array
-	String = JSON_STRING,		//!< String
-	Integer = JSON_INTEGER,		//!< Integer
-	Real = JSON_REAL,		//!< Floating point
-	True = JSON_TRUE,		//!< Boolean true
-	False = JSON_FALSE,		//!< Boolean false
-	Null = JSON_NULL		//!< Empty or null
+	Array,		//!< Value is an array []
+	Boolean,	//!< Value is boolean
+	Number,		//!< Value is number
+	Object,		//!< Value is object {}
+	String,		//!< Value is unicode string
+	Null,		//!< Value is defined to null
+	Undefined	//!< Value is undefined
 };
 
 /**
  * @class Error
- * @brief Error thrown for any error
+ * @brief Error description.
  */
-class Error final : public std::exception {
-private:
-	std::string	m_text;
-	std::string	m_source;
-	int		m_line{};
-	int		m_column{};
-	int		m_position{};
+class Error {
+public:
+	std::string text;	//!< The error message
+	std::string source;	//!< The source (e.g. name of file)
+	int line{0};		//!< The line number
+	int column{0};		//!< The column
+	int position{0};	//!< The position
+};
+
+class Array;
+class Object;
+
+/**
+ * @class Value
+ * @brief Generic JSON value wrapper.
+ */
+class Value {
+protected:
+	Type m_type;
+
+	union {
+		double m_number;
+		bool m_boolean;
+		std::string m_string;
+		std::vector<Value> m_array;
+		std::map<std::string, Value> m_map;
+	};
 
 public:
 	/**
-	 * Custom error with no line, no column and no position.
-	 *
-	 * @param error the error message
+	 * Construct an undefined value.
 	 */
-	inline Error(std::string error)
-		: m_text(std::move(error))
+	inline Value() noexcept
+		: m_type{Type::Undefined}
 	{
 	}
 
 	/**
-	 * Error from a json_error_t.
-	 *
-	 * @param error the error
+	 * Construct a null value.
 	 */
-	inline Error(const json_error_t &error)
-		: m_text(error.text)
-		, m_source(error.source)
-		, m_line(error.line)
-		, m_column(error.column)
-		, m_position(error.position)
+	inline Value(std::nullptr_t) noexcept
+		: m_type{Type::Null}
+	{
+	}
+
+	/**
+	 * Construct a boolean value.
+	 *
+	 * @param value the boolean value
+	 */
+	inline Value(bool value) noexcept
+		: m_type{Type::Boolean}
+		, m_boolean{value}
 	{
 	}
 
 	/**
-	 * Get the error message.
+	 * Construct a value from a C-string.
 	 *
-	 * @return the message
+	 * @param value the C-string
 	 */
-	const char *what() const noexcept override
+	inline Value(const char *value)
+		: m_type{Type::String}
 	{
-		return m_text.c_str();
-	}
-
-	/**
-	 * Get the text message.
-	 *
-	 * @return the text
-	 */
-	inline const std::string &text() const noexcept
-	{
-		return m_text;
+		new (&m_string) std::string{value};
 	}
 
 	/**
-	 * Get the source.
+	 * Construct a number value.
 	 *
-	 * @return the source
+	 * @param value the real value
 	 */
-	inline const std::string &source() const noexcept
+	inline Value(double value) noexcept
+		: m_type{Type::Number}
+		, m_number{value}
 	{
-		return m_source;
 	}
 
 	/**
-	 * Get the line.
+	 * Construct a string value.
 	 *
-	 * @return the line
+	 * @param value the string
 	 */
-	inline int line() const noexcept
+	inline Value(std::string value) noexcept
+		: m_type{Type::String}
 	{
-		return m_line;
-	}
-
-	/**
-	 * Get the column.
-	 *
-	 * @return the column
-	 */
-	inline int column() const noexcept
-	{
-		return m_column;
+		new (&m_string) std::string{std::move(value)};
 	}
 
 	/**
-	 * Get the position.
+	 * Move constructor.
 	 *
-	 * @return the position
+	 * @param other the value to move from
 	 */
-	inline int position() const noexcept
+	inline Value(Value &&other)
 	{
-		return m_position;
-	}
-};
-
-class Object;
-class Array;
-
-/**
- * @class Value
- * @brief Encapsulate any JSON value
- */
-class Value {
-public:
-	using Handle = std::unique_ptr<json_t, void (*)(json_t *)>;
-
-	friend class Object;
-	friend class Array;
+		m_type = other.m_type;
 
-protected:
-	/**
-	 * The unique_ptr handle of json_t, will automatically decrease
-	 * the reference count in its deleter.
-	 */
-	Handle m_handle;
-
-	inline void check() const
-	{
-		if (m_handle == nullptr)
-			throw Error(std::strerror(errno));
-	}
-
-public:
-	/**
-	 * Deep copy of that element.
-	 *
-	 * @param value the other value
-	 * @throw Error on allocation error
-	 */
-	inline Value(const Value &value)
-		: m_handle(json_deep_copy(value.m_handle.get()), json_decref)
-	{
-		check();
+		switch (m_type) {
+		case Type::String:
+			new (&m_string) std::string();
+			m_string = std::move(other.m_string);
+			break;
+		case Type::Number:
+			m_number = other.m_number;
+			break;
+		case Type::Boolean:
+			m_boolean = other.m_boolean;
+			break;
+		case Type::Object:
+			new (&m_map) std::map<std::string, Value>();
+			m_map = std::move(other.m_map);
+			break;
+		case Type::Array:
+			new (&m_array) std::vector<Value>();
+			m_array = std::move(other.m_array);
+			break;
+		default:
+			break;
+		}
 	}
 
 	/**
-	 * Assign a deep copy of the other element.
+	 * Copy constructor.
 	 *
-	 * @return *this
-	 * @throw Error on allocation error
+	 * @param other the value to copy from
 	 */
-	inline Value &operator=(const Value &value)
+	inline Value(const Value &other)
 	{
-		m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref);
+		m_type = other.m_type;
 
-		check();
-
-		return *this;
+		switch (m_type) {
+		case Type::String:
+			new (&m_string) std::string();
+			m_string = other.m_string;
+			break;
+		case Type::Number:
+			m_number = other.m_number;
+			break;
+		case Type::Boolean:
+			m_boolean = other.m_boolean;
+			break;
+		case Type::Object:
+			new (&m_map) std::map<std::string, Value>();
+			m_map = other.m_map;
+			break;
+		case Type::Array:
+			new (&m_array) std::vector<Value>();
+			m_array = other.m_array;
+			break;
+		default:
+			break;
+		}
 	}
 
 	/**
-	 * Move constructor, the other value is left empty (Type::Null).
+	 * Copy operator.
 	 *
-	 * @param other the other value
+	 * @param other the value to copy from
+	 * @return *this
 	 */
-	inline Value(Value &&other) noexcept
-		: m_handle(std::move(other.m_handle))
+	inline Value &operator=(const Value &other)
 	{
-		other.m_handle = Handle(json_null(), json_decref);
-	}
+		m_type = other.m_type;
 
-	/**
-	 * Move assignment, the other value is left empty (Type::Null).
-	 *
-	 * @param other the other value
-	 */
-	inline Value &operator=(Value &&other) noexcept
-	{
-		m_handle = std::move(other.m_handle);
-		other.m_handle = Handle(json_null(), json_decref);
+		switch (m_type) {
+		case Type::String:
+			new (&m_string) std::string();
+			m_string = other.m_string;
+			break;
+		case Type::Number:
+			m_number = other.m_number;
+			break;
+		case Type::Boolean:
+			m_boolean = other.m_boolean;
+			break;
+		case Type::Object:
+			new (&m_map) std::map<std::string, Value>();
+			m_map = other.m_map;
+			break;
+		case Type::Array:
+			new (&m_array) std::vector<Value>();
+			m_array = other.m_array;
+			break;
+		default:
+			break;
+		}
 
 		return *this;
 	}
 
 	/**
-	 * Create a Value from a native Jansson type. This function
-	 * will increment the json_t reference count.
+	 * Move operator.
 	 *
-	 * @param json the value
-	 */
-	inline Value(json_t *json) noexcept
-		: m_handle(json, json_decref)
-	{
-	}
-
-	/**
-	 * Construct a null value from a nullptr argument.
-	 */
-	inline Value(std::nullptr_t) noexcept
-		: m_handle(json_null(), json_decref)
-	{
-	}
-
-	/**
-	 * Create an empty value (Type::Null).
+	 * @param other the value to move from
 	 */
-	inline Value() noexcept
-		: m_handle(json_null(), json_decref)
-	{
-	}
-
-	/**
-	 * Create a boolean value.
-	 *
-	 * @param value the value
-	 */
-	inline Value(bool value) noexcept
-		: m_handle(json_boolean(value), json_decref)
+	inline Value &operator=(Value &&other)
 	{
-	}
+		m_type = other.m_type;
 
-	/**
-	 * Create a integer value (Type::Integer).
-	 *
-	 * @param value the value
-	 * @throw Error on allocation error
-	 */
-	inline Value(int value)
-		: m_handle(json_integer(value), json_decref)
-	{
-		check();
+		switch (m_type) {
+		case Type::String:
+			new (&m_string) std::string();
+			m_string = std::move(other.m_string);
+			break;
+		case Type::Number:
+			m_number = other.m_number;
+			break;
+		case Type::Boolean:
+			m_boolean = other.m_boolean;
+			break;
+		case Type::Object:
+			new (&m_map) std::map<std::string, Value>();
+			m_map = std::move(other.m_map);
+			break;
+		case Type::Array:
+			new (&m_array) std::vector<Value>();
+			m_array = std::move(other.m_array);
+			break;
+		default:
+			break;
+		}
+
+		return *this;
 	}
 
 	/**
-	 * Create a real value (Type::Real).
-	 *
-	 * @param value the value
-	 * @throw Error on allocation error
+	 * Destructor.
 	 */
-	inline Value(double value)
-		: m_handle(json_real(value), json_decref)
+	inline ~Value()
 	{
-		check();
-	}
-
-	/**
-	 * Create a string value (Type::String).
-	 *
-	 * @param value the value
-	 * @throw Error on allocation error
-	 */
-	inline Value(std::string value)
-		: m_handle(json_string(value.c_str()), json_decref)
-	{
-		check();
+		switch (m_type) {
+		case Type::String:
+			m_string.~basic_string();
+			break;
+		case Type::Object:
+			m_map.~map<std::string, Value>();
+			break;
+		case Type::Array:
+			m_array.~vector<Value>();
+			break;
+		default:
+			break;
+		}
 	}
 
 	/**
-	 * Create from a C string (Type::String).
-	 *
-	 * @param value the string
-	 * @throw Error on allocation error
-	 */
-	inline Value(const char *value)
-		: m_handle(json_string(value), json_decref)
-	{
-		check();
-	}
-
-	/**
-	 * Create from a string literal (Type::String).
-	 *
-	 * @param value the value
-	 * @throw Error on allocation error
-	 */
-	template <size_t Size>
-	inline Value(char (&value)[Size])
-		: m_handle(json_string(value), json_decref)
-	{
-		check();
-	}
-
-	/**
-	 * Default destructor.
-	 */
-	virtual ~Value() = default;
-
-	/**
-	 * Get the type of value.
+	 * Get the value type.
 	 *
 	 * @return the type
 	 */
 	inline Type typeOf() const noexcept
 	{
-		return static_cast<Type>(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());
+		return m_type;
 	}
 
 	/**
-	 * Tells if the json value is an JSON_NULL.
+	 * Get the value as boolean.
 	 *
-	 * @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
+	 * @return the value or false if not a boolean
 	 */
-	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());
-	}
+	bool toBool() const noexcept;
 
 	/**
-	 * Tells if the json value is an JSON_TRUE or JSON_FALSE.
+	 * Get the value as real.
 	 *
-	 * @return true or false
+	 * @return the value or 0 if not a real
 	 */
-	inline bool isBoolean() const noexcept
-	{
-		return json_is_boolean(m_handle.get());
-	}
-
-	/**
-	 * 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;
-	}
+	double toNumber() const noexcept;
 
 	/**
-	 * Get the integer value.
+	 * Get the value as string.
 	 *
-	 * @return the value or 0
+	 * @return the value or empty stirng if not a string
 	 */
-	inline int toInteger() const noexcept
-	{
-		return json_integer_value(m_handle.get());
-	}
+	std::string toString() const noexcept;
 
 	/**
-	 * Get the real value.
+	 * Convert the value to object.
 	 *
-	 * @return the value or 0
-	 */
-	inline double toReal() const noexcept
-	{
-		return json_real_value(m_handle.get());
-	}
-
-	/**
-	 * Convert to object.
-	 *
-	 * @return an object
+	 * @return an object or empty if not an object
+	 * @note the returned object is a copy, modifying it won't modify this value
 	 */
 	Object toObject() const noexcept;
 
 	/**
-	 * Convert to array.
+	 * Convert the value to array.
 	 *
-	 * @return an array
+	 * @return an array or empty if not an array
+	 * @note the returned array is a copy, modifying it won't modify this value
 	 */
 	Array toArray() const noexcept;
+};
 
+/**
+ * @class Array
+ * @brief Array definition.
+ */
+class Array : public Value {
+public:
 	/**
-	 * Write to a stream.
-	 *
-	 * @param out the out
-	 * @param flags the optional Jansson flags
+	 * Construct an empty array.
 	 */
-	inline void write(std::ofstream &out, int flags = 0) const
+	Array()
 	{
-		auto content = dump(flags);
-
-		std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator<char>(out));
+		m_type = Type::Array;
+		new (&m_array) std::vector<Value>();
 	}
 
 	/**
-	 * Overloaded function.
+	 * Copy constructor from value for safe casts.
+	 *
+	 * @param v the value to copy from
+	 */
+	Array(const Value &v)
+		: Value{v}
+	{
+	}
+
+	/**
+	 * Move constructor from value for safe casts.
 	 *
-	 * @param out the out
-	 * @param flags the optional Jansson flags
+	 * @param v the value to move from
+	 */
+	Array(Value &&v)
+		: Value{std::move(v)}
+	{
+	}
+
+	/**
+	 * Get non-const iterator to the beginning.
+	 *
+	 * @return the iterator
 	 */
-	inline void write(std::ofstream &&out, int flags = 0) const
+	inline auto begin() noexcept
 	{
-		write(out, flags);
+		return m_array.begin();
+	}
+
+	/**
+	 * Get a const iterator to the beginning.
+	 *
+	 * @return the iterator
+	 */
+	inline auto begin() const noexcept
+	{
+		return m_array.begin();
 	}
 
 	/**
-	 * Convert the Json value as a string.
+	 * Get a const iterator to the beginning.
 	 *
-	 * @return the string
-	 * @param flags the optional Jansson flags
+	 * @return the iterator
 	 */
-	inline std::string dump(int flags = 0) const
+	inline auto cbegin() const noexcept
 	{
-		auto str = json_dumps(m_handle.get(), flags);
-
-		if (str == nullptr)
-			return "";
+		return m_array.cbegin();
+	}
 
-		std::string ret(str);
-		std::free(str);
+	/**
+	 * Get a non-const iterator to the end.
+	 *
+	 * @return the iterator
+	 */
+	inline auto end() noexcept
+	{
+		return m_array.end();
+	}
 
-		return ret;
+	/**
+	 * Get a const iterator to the end.
+	 *
+	 * @return the iterator
+	 */
+	inline auto end() const noexcept
+	{
+		return m_array.end();
 	}
 
 	/**
-	 * Convert to native Jansson type.
+	 * Get a const iterator to the end.
 	 *
-	 * You should not call json_incref or json_decref on it as it is
-	 * automatically done.
+	 * @return the iterator
+	 */
+	inline auto cend() const noexcept
+	{
+		return m_array.cend();
+	}
+
+	/**
+	 * Get a value at the specified index.
 	 *
-	 * @return the json_t handle
-	 * @warning use this function with care
+	 * @param position the position
+	 * @return the value
+	 * @throw std::out_of_range if out of bounds
 	 */
-	inline operator json_t *() noexcept
+	inline const Value &at(unsigned position) const
 	{
-		return m_handle.get();
+		return m_array.at(position);
+	}
+
+	/**
+	 * Get a value at the specified index.
+	 *
+	 * @param position the position
+	 * @return the value
+	 * @throw std::out_of_range if out of bounds
+	 */
+	inline Value &at(unsigned position)
+	{
+		return m_array.at(position);
+	}
+
+	/**
+	 * Get a value at the specified index.
+	 *
+	 * @param position the position
+	 * @return the value
+	 */
+	inline const Value &operator[](unsigned position) const
+	{
+		return m_array[position];
 	}
 
 	/**
 	 * Overloaded function.
 	 *
-	 * @return the json_t handle
+	 * @param position the position
+	 * @return the value
 	 */
-	inline operator const json_t *() const noexcept
+	inline Value &operator[](unsigned position)
 	{
-		return m_handle.get();
+		return m_array[position];
 	}
 
 	/**
-	 * Equality operator.
+	 * Add a new value.
+	 *
+	 * @param value the value to append
 	 */
-	inline bool operator==(const Value &other) const noexcept
+	template <typename T>
+	inline void append(T &&value)
 	{
-		return json_equal(m_handle.get(), other.m_handle.get());
+		m_array.push_back(std::forward<T>(value));
 	}
 };
 
 /**
- * @class Array
- * @brief Manipulate JSON arrays
+ * @class Object
+ * @brief Object definition.
  */
-class Array final : public Value {
+class Object : public Value {
 public:
 	/**
-	 * @class Ref
-	 * @brief Reference wrapper to be assignable
+	 * Construct an empty object.
 	 */
-	class Ref final : public Value {
-	private:
-		Array &m_array;
-		int m_index;
-
-	public:
-		explicit inline Ref(Value value, Array &array, int index)
-			: Value(std::move(value))
-			, m_array(array)
-			, m_index(index)
-		{
-		}
-
-		inline operator Value() const noexcept
-		{
-			return *this;
-		}
-
-		inline Value &operator*() noexcept
-		{
-			return *this;
-		}
-
-		inline Value *operator->() noexcept
-		{
-			return this;
-		}
-
-		inline Ref &operator=(const Value &value)
-		{
-			m_array.replace(value, m_index);
-
-			return *this;
-		}
-
-		inline Ref &operator=(Value &&value)
-		{
-			m_array.replace(std::move(value), m_index);
-
-			return *this;
-		}
-	};
+	Object()
+	{
+		m_type = Type::Object;
+		new (&m_map) std::map<std::string, Value>();
+	}
 
 	/**
-	 * @class Ptr
-	 * @brief Pointer wrapper for Value iterators
+	 * Copy constructor from value for safe casts.
+	 *
+	 * @param v the value to copy from
 	 */
-	class Ptr final : public Value {
-	public:
-		explicit Ptr(Value value) noexcept
-			: Value(std::move(value))
-		{
-		}
-
-		inline Value &operator*() noexcept
-		{
-			return *this;
-		}
-
-		inline Value *operator->() noexcept
-		{
-			return this;
-		}
-	};
-
-	class iterator final {
-	public:
-		using iterator_category = std::random_access_iterator_tag;
-		using difference_type = int;
-		using value_type = Value;
-		using reference = Ref;
-		using pointer = Ptr;
-
-		friend class Array;
-
-	private:
-		Array &m_array;
-		int m_index;
-
-	public:
-		explicit inline iterator(Array &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;
-		}
-	};
+	Object(const Value &v)
+		: Value{v}
+	{
+	}
 
-	class const_iterator final {
-	public:
-		using iterator_category = std::random_access_iterator_tag;
-		using difference_type = int;
-		using value_type = Value;
-		using reference = Value;
-		using pointer = Ptr;
-
-		friend class Array;
-
-	private:
-		const Array &m_array;
-		int m_index;
-
-	public:
-		explicit inline const_iterator(const Array &array, int index = 0) noexcept
-			: m_array(array)
-			, m_index(index)
-		{
-		}
-
-		inline Value operator*() const
-		{
-			return m_array.at(m_index);
-		}
-
-		inline Ptr operator->() const
-		{
-			return Ptr(m_array.at(m_index));
-		}
-
-		inline Value 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;
-		}
+	/**
+	 * Move constructor from value for safe casts.
+	 *
+	 * @param v the value to move from
+	 */
+	Object(Value &&v)
+		: Value{std::move(v)}
+	{
+	}
 
-		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 = Value;
-	using const_pointer = const value_type *;
-	using reference = Value &;
-	using const_reference = const Value &;
-	using difference_type = int;
-
-protected:
-	using Value::Value;
-
-public:
 	/**
-	 * Create an empty array.
+	 * Get non-const iterator to the beginning.
 	 *
-	 * @throw Error on allocation error
+	 * @return the iterator
 	 */
-	inline Array()
-		: Value(json_array())
+	inline auto begin() noexcept
 	{
-		check();
+		return m_map.begin();
 	}
 
 	/**
-	 * Create an array from a list of values.
-	 *
-	 * @param list the list
-	 * @throw Error on allocation error
-	 */
-	inline Array(std::initializer_list<value_type> list)
-		: Array()
-	{
-		for (auto &v : list)
-			append(std::move(v));
-	}
-
-	/**
-	 * Returns an iterator to the beginning.
+	 * Get a const iterator to the beginning.
 	 *
 	 * @return the iterator
 	 */
-	inline iterator begin() noexcept
+	inline auto begin() const noexcept
 	{
-		return iterator(*this, 0);
+		return m_map.begin();
 	}
 
 	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline const_iterator begin() const noexcept
-	{
-		return const_iterator(*this, 0);
-	}
-
-	/**
-	 * Overloaded function.
+	 * Get a const iterator to the beginning.
 	 *
 	 * @return the iterator
 	 */
-	inline const_iterator cbegin() const noexcept
+	inline auto cbegin() const noexcept
 	{
-		return const_iterator(*this, 0);
+		return m_map.cbegin();
 	}
 
 	/**
-	 * Returns an iterator to the end.
+	 * Get a non-const iterator to the end.
 	 *
 	 * @return the iterator
 	 */
-	inline iterator end() noexcept
+	inline auto end() noexcept
 	{
-		return iterator(*this, size());
+		return m_map.end();
 	}
 
 	/**
-	 * Overloaded function.
+	 * Get a const iterator to the end.
 	 *
 	 * @return the iterator
 	 */
-	inline const_iterator end() const noexcept
+	inline auto end() const noexcept
 	{
-		return const_iterator(*this, size());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	inline const_iterator cend() const noexcept
-	{
-		return const_iterator(*this, size());
+		return m_map.end();
 	}
 
 	/**
-	 * Get a value.
+	 * Get a const iterator to the end.
 	 *
-	 * @param index the index
-	 * @throw Error on error
+	 * @return the iterator
 	 */
-	Value at(int index) const;
-
-	/**
-	 * Erase the array content.
-	 */
-	inline void clear() noexcept
+	inline auto cend() const noexcept
 	{
-		json_array_clear(m_handle.get());
+		return m_map.cend();
 	}
 
 	/**
-	 * Remove the element at the specified index.
+	 * Get a value from the object.
 	 *
-	 * @param index the index
+	 * @param name the value key
+	 * @return the value
+	 * @throw std::out_of_range if not found
 	 */
-	inline void erase(int index) noexcept
+	inline const Value &at(const std::string &name) const
 	{
-		json_array_remove(m_handle.get(), index);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param it the iterator
-	 */
-	inline void erase(iterator it) noexcept
-	{
-		erase(it.m_index);
+		return m_map.at(name);
 	}
 
 	/**
 	 * Overloaded function.
 	 *
-	 * @param it the iterator
-	 */
-	inline void erase(const_iterator it) noexcept
-	{
-		erase(it.m_index);
-	}
-
-	/**
-	 * Get the number of values in the array
-	 *
-	 * @return the number or 0
+	 * @param name the value key
+	 * @return the value
+	 * @throw std::out_of_range if not found
 	 */
-	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 Value &value) noexcept
+	inline Value &at(const std::string &name)
 	{
-		json_array_insert(m_handle.get(), 0, value.m_handle.get());
-	}
-
-	/**
-	 * Insert a copy of the value at the end.
-	 *
-	 * @param value the value to insert
-	 */
-	inline void append(const Value &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
-	 */
-	inline void insert(const Value &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 Value &value, int index)
-	{
-		json_array_set(m_handle.get(), index, value.m_handle.get());
+		return m_map.at(name);
 	}
 
 	/**
-	 * Get the value at the specified index.
+	 * Get a value from the object.
 	 *
-	 * @param index the position
+	 * @param name the value key
 	 * @return the value
 	 */
-	Value 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 Object
- * @brief Object wrapper
- */
-class Object final : public Value {
-public:
-	using key_type = std::string;
-	using mapped_type = Value;
-	using size_type = int;
-	using value_type = std::pair<key_type, mapped_type>;
-	using const_pointer = const value_type *;
-	using reference = Value &;
-	using const_reference = const Value &;
-	using difference_type = int;
-
-	/**
-	 * @class Ref
-	 * @brief Wrapper for updating Object
-	 *
-	 * This class is only used for the following functions:
-	 *
-	 *	Object::operator[]
-	 */
-	class Ref final : public Value {
-	private:
-		Object &m_object;
-		std::string m_key;
-
-	public:
-		explicit inline Ref(Value value, Object &object, std::string key)
-			: Value(std::move(value))
-			, m_object(object)
-			, m_key(std::move(key))
-		{
-		}
-
-		inline operator Value() const noexcept
-		{
-			return *this;
-		}
-
-		inline Value &operator*() noexcept
-		{
-			return *this;
-		}
-
-		inline Value *operator->() noexcept
-		{
-			return this;
-		}
-
-		inline Ref &operator=(const Value &value)
-		{
-			m_object.set(m_key, value);
-
-			return *this;
-		}
-
-		inline Ref &operator=(Value &&value)
-		{
-			m_object.set(m_key, std::move(value));
-
-			return *this;
-		}
-	};
-
-	/**
-	 * @class Ptr
-	 * @brief Pointer wrapper for Value iterators
-	 *
-	 * For const iterators, the real type is a Value, 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>;
-
-		friend class Object;
-
-	private:
-		Object &m_object;
-		void *m_keyIt;
-
-		inline std::string key() const noexcept
-		{
-			return json_object_iter_key(m_keyIt);
-		}
-
-	public:
-		explicit inline iterator(Object &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 bool operator==(iterator other) const noexcept
-		{
-			return m_keyIt == other.m_keyIt;
-		}
-
-		inline bool 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, Value>;
-
-		friend class Object;
-
-	private:
-		const Object &m_object;
-		void *m_keyIt;
-
-		inline std::string key() const noexcept
-		{
-			return json_object_iter_key(m_keyIt);
-		}
-
-	public:
-		explicit inline const_iterator(const Object &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<Value> operator->() const
-		{
-			auto k = key();
-
-			return Ptr<Value>({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 bool operator==(const_iterator other) const noexcept
-		{
-			return m_keyIt == other.m_keyIt;
-		}
-
-		inline bool operator!=(const_iterator other) const noexcept
-		{
-			return m_keyIt != other.m_keyIt;
-		}
-	};
-
-protected:
-	using Value::Value;
-
-public:
-	/**
-	 * Create empty object.
-	 *
-	 * @throw Error on allocation error
-	 */
-	inline Object()
-		: Value(json_object())
+	inline const Value &operator[](const std::string &name) const
 	{
-		check();
-	}
-
-	/**
-	 * Create a Object from an initializer_list.
-	 *
-	 * @param list the list of key-value pairs
-	 * @throw Error on allocation error
-	 */
-	inline Object(std::initializer_list<value_type> list)
-		: Object()
-	{
-		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()));
+		return m_map[name];
 	}
 
 	/**
 	 * Overloaded function.
 	 *
-	 * @return the iterator
-	 */
-	inline const_iterator begin() const noexcept
-	{
-		return const_iterator(*this, json_object_iter(m_handle.get()));
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
+	 * @param name the value key
+	 * @return the value
 	 */
-	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
+	inline Value &operator[](const std::string &name)
 	{
-		return iterator(*this, nullptr);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @return the iterator
-	 */
-	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;
+		return m_map[name];
 	}
 
 	/**
-	 * 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());
-	}
-
-	/**
-	 * Remove all elements from the object.
-	 */
-	inline void clear() noexcept
-	{
-		json_object_clear(m_handle.get());
-	}
-
-	/**
-	 * Remove element `key' if exists.
+	 * Insert a new value.
 	 *
-	 * @param key the key
-	 */
-	inline void erase(const std::string &key) noexcept
-	{
-		json_object_del(m_handle.get(), key.c_str());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param it the iterator
-	 */
-	inline void erase(iterator it) noexcept
-	{
-		erase(it.key());
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param it the iterator
-	 */
-	inline void erase(const_iterator it) noexcept
-	{
-		erase(it.key());
-	}
-
-	/**
-	 * Set the value as key in the object.
-	 *
-	 * @param key the key
+	 * @param name the key
 	 * @param value the value
 	 */
-	inline void set(const std::string &key, const Value &value) noexcept
+	inline void insert(std::string name, Value value)
 	{
-		json_object_set(m_handle.get(), key.c_str(), value.m_handle.get());
+		m_map.insert({std::move(name), std::move(value)});
 	}
+};
 
-	/**
-	 * 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);
+/**
+ * @class Buffer
+ * @brief Open JSON document from text.
+ */
+class Buffer {
+public:
+	std::string text;	//!< The JSON text
+};
 
-	/**
-	 * Get the value at the specified key. If the value is not found, an
-	 * empty value is returned
-	 *
-	 * @param key the key
-	 * @return the value
-	 */
-	Value operator[](const std::string &key) const;
+/**
+ * @class File
+ * @brief Open JSON document from a file.
+ */
+class File {
+public:
+	std::string path;	//!< The path to the file
 };
 
 /**
  * @class Document
- * @brief Read files and strings to create Json values
+ * @brief Construct a JSON document from a source
+ * @see Buffer
+ * @see File
  */
-class Document final {
+class Document {
 private:
 	Value m_value;
 
-	Value read(std::string, int flags) const;
-	Value read(std::ifstream &stream, int flags) const;
-
 public:
 	/**
-	 * Construct a document from a file.
+	 * Construct a document from a buffer.
 	 *
-	 * @param stream the stream
-	 * @param flags the optional Jansson flags
+	 * @param buffer the text
 	 * @throw Error on errors
 	 */
-	Document(std::ifstream &fstream, int flags = 0);
+	Document(Buffer buffer);
 
 	/**
 	 * Construct a document from a file.
 	 *
-	 * @param stream the stream
-	 * @param flags the optional Jansson flags
+	 * @param file the file
 	 * @throw Error on errors
 	 */
-	Document(std::ifstream &&stream, int flags = 0);
+	Document(File file);
 
 	/**
-	 * Construct a document from a file.
-	 *
-	 * @param stream the stream
-	 * @param flags the optional Jansson flags
-	 * @throw Error on errors
-	 */
-	Document(std::string content, int flags = 0);
-
-	/**
-	 * Check if the document contains an object.
+	 * Check if the opened document is an object.
 	 *
 	 * @return true if object
 	 */
 	inline bool isObject() const noexcept
 	{
-		return m_value.isObject();
+		return m_value.typeOf() == Type::Object;
 	}
 
 	/**
-	 * Check if the document contains an array.
+	 * Check if the opened document is an array.
 	 *
 	 * @return true if array
 	 */
 	inline bool isArray() const noexcept
 	{
-		return m_value.isArray();
+		return m_value.typeOf() == Type::Array;
 	}
 
 	/**
-	 * Convert the document value to object
+	 * Get the object.
 	 *
-	 * @return the value as object
+	 * @return the object or empty object if not an object
 	 */
 	inline Object toObject() const noexcept
 	{
-		return m_value.toObject();
+		if (m_value.typeOf() != Type::Object) {
+			return Object{};
+		}
+
+		return Object(m_value);
 	}
 
 	/**
-	 * Convert the document value to array
+	 * Get the array.
 	 *
-	 * @return the value as array
+	 * @return the array or empty object if not an array
 	 */
 	inline Array toArray() const noexcept
 	{
-		return m_value.toArray();
+		if (m_value.typeOf() != Type::Array) {
+			return Array{};
+		}
+
+		return Array(m_value);
 	}
 };
 
 } // !json
 
-#endif // !_JSON_H_
+#endif // !_JSON_H_
\ No newline at end of file