worked on issue #8 and #27

This commit is contained in:
Niels 2015-01-24 20:33:06 +01:00
parent 47e3a3ed24
commit 5199382c91
4 changed files with 248 additions and 59 deletions

View file

@ -140,7 +140,7 @@ class json
/// create an object according to given type /// create an object according to given type
json(const value_t); json(const value_t);
/// create a null object /// create a null object
json() = default; json() noexcept;
/// create a null object /// create a null object
json(std::nullptr_t) noexcept; json(std::nullptr_t) noexcept;
/// create a string object from a C++ string /// create a string object from a C++ string
@ -160,7 +160,7 @@ class json
/// create an object (move) /// create an object (move)
json(object_t&&); json(object_t&&);
/// create from an initializer list (to an array or object) /// create from an initializer list (to an array or object)
json(list_init_t); json(list_init_t, bool = true, value_t = value_t::array);
/*! /*!
@brief create a number object (integer) @brief create a number object (integer)
@ -171,7 +171,7 @@ class json
std::numeric_limits<T>::is_integer, T>::type std::numeric_limits<T>::is_integer, T>::type
= 0> = 0>
json(const T n) noexcept json(const T n) noexcept
: type_(value_t::number), : final_type_(0), type_(value_t::number),
value_(static_cast<number_t>(n)) value_(static_cast<number_t>(n))
{} {}
@ -184,7 +184,7 @@ class json
std::is_floating_point<T>::value>::type std::is_floating_point<T>::value>::type
> >
json(const T n) noexcept json(const T n) noexcept
: type_(value_t::number_float), : final_type_(0), type_(value_t::number_float),
value_(static_cast<number_float_t>(n)) value_(static_cast<number_float_t>(n))
{} {}
@ -229,6 +229,11 @@ class json
/// destructor /// destructor
~json() noexcept; ~json() noexcept;
/// explicit keyword to force array creation
static json array(list_init_t = list_init_t());
/// explicit keyword to force object creation
static json object(list_init_t = list_init_t());
/// create from string representation /// create from string representation
static json parse(const std::string&); static json parse(const std::string&);
/// create from string representation /// create from string representation
@ -404,9 +409,10 @@ class json
const_reverse_iterator crend() const noexcept; const_reverse_iterator crend() const noexcept;
private: private:
/// whether the type is final
unsigned final_type_ : 1;
/// the type of this object /// the type of this object
value_t type_ = value_t::null; value_t type_ = value_t::null;
/// the payload /// the payload
value value_ {}; value value_ {};
@ -654,6 +660,9 @@ json::json(const value_t t)
} }
} }
json::json() noexcept : final_type_(0), type_(value_t::null)
{}
/*! /*!
Construct a null JSON object. Construct a null JSON object.
*/ */
@ -666,35 +675,35 @@ Construct a string JSON object.
@param s a string to initialize the JSON object with @param s a string to initialize the JSON object with
*/ */
json::json(const std::string& s) json::json(const std::string& s)
: type_(value_t::string), value_(new string_t(s)) : final_type_(0), type_(value_t::string), value_(new string_t(s))
{} {}
json::json(std::string&& s) json::json(std::string&& s)
: type_(value_t::string), value_(new string_t(std::move(s))) : final_type_(0), type_(value_t::string), value_(new string_t(std::move(s)))
{} {}
json::json(const char* s) json::json(const char* s)
: type_(value_t::string), value_(new string_t(s)) : final_type_(0), type_(value_t::string), value_(new string_t(s))
{} {}
json::json(const bool b) noexcept json::json(const bool b) noexcept
: type_(value_t::boolean), value_(b) : final_type_(0), type_(value_t::boolean), value_(b)
{} {}
json::json(const array_t& a) json::json(const array_t& a)
: type_(value_t::array), value_(new array_t(a)) : final_type_(0), type_(value_t::array), value_(new array_t(a))
{} {}
json::json(array_t&& a) json::json(array_t&& a)
: type_(value_t::array), value_(new array_t(std::move(a))) : final_type_(0), type_(value_t::array), value_(new array_t(std::move(a)))
{} {}
json::json(const object_t& o) json::json(const object_t& o)
: type_(value_t::object), value_(new object_t(o)) : final_type_(0), type_(value_t::object), value_(new object_t(o))
{} {}
json::json(object_t&& o) json::json(object_t&& o)
: type_(value_t::object), value_(new object_t(std::move(o))) : final_type_(0), type_(value_t::object), value_(new object_t(std::move(o)))
{} {}
/*! /*!
@ -711,33 +720,90 @@ as is to create an array.
@bug With the described approach, we would fail to recognize an array whose @bug With the described approach, we would fail to recognize an array whose
first element is again an arrays as array. first element is again an arrays as array.
@param a an initializer list to create from
@param type_deduction whether the type (array/object) shall eb deducted
@param manual_type if type deduction is switched of, pass a manual type
*/ */
json::json(list_init_t a) json::json(list_init_t a, bool type_deduction, value_t manual_type) : final_type_(0)
{ {
// the initializer list could describe an object
bool is_object = true;
// check if each element is an array with two elements whose first element // check if each element is an array with two elements whose first element
// is a string // is a string
for (const auto& element : a) for (const auto& element : a)
{ {
if (element.type_ != value_t::array or if ((element.final_type_ == 1 and element.type_ == value_t::array)
element.size() != 2 or or (element.type_ != value_t::array or element.size() != 2 or element[0].type_ != value_t::string))
element[0].type_ != value_t::string)
{ {
// we found an element that makes it impossible to use the
// the initializer list describes an array // initializer list as object
type_ = value_t::array; is_object = false;
value_ = new array_t(a); break;
return;
} }
} }
// the initializer list is a list of pairs // adjust type if type deduction is not wanted
type_ = value_t::object; if (not type_deduction)
value_ = new object_t();
for (const json& element : a)
{ {
const std::string k = element[0]; // mark this object's type as final
value_.object->emplace(std::make_pair(std::move(k), final_type_ = 1;
std::move(element[1])));
// if array is wanted, do not create an object though possible
if (manual_type == value_t::array)
{
is_object = false;
}
// if object is wanted but impossible, throw an exception
if (manual_type == value_t::object and not is_object)
{
throw std::logic_error("cannot create JSON object");
}
}
if (is_object)
{
// the initializer list is a list of pairs -> create object
type_ = value_t::object;
value_ = new object_t();
for (auto& element : a)
{
value_.object->emplace(std::make_pair(std::move(element[0]), std::move(element[1])));
}
}
else
{
// the initializer list describes an array -> create array
type_ = value_t::array;
value_ = new array_t(std::move(a));
}
}
/*!
@param a initializer list to create an array from
@return array
*/
json json::array(list_init_t a)
{
return json(a, false, value_t::array);
}
/*!
@param a initializer list to create an object from
@return object
*/
json json::object(list_init_t a)
{
// if more than one element is in the initializer list, wrap it
if (a.size() > 1)
{
return json({a}, false, value_t::object);
}
else
{
return json(a, false, value_t::object);
} }
} }

View file

@ -84,6 +84,9 @@ json::json(const value_t t)
} }
} }
json::json() noexcept : final_type_(0), type_(value_t::null)
{}
/*! /*!
Construct a null JSON object. Construct a null JSON object.
*/ */
@ -96,35 +99,35 @@ Construct a string JSON object.
@param s a string to initialize the JSON object with @param s a string to initialize the JSON object with
*/ */
json::json(const std::string& s) json::json(const std::string& s)
: type_(value_t::string), value_(new string_t(s)) : final_type_(0), type_(value_t::string), value_(new string_t(s))
{} {}
json::json(std::string&& s) json::json(std::string&& s)
: type_(value_t::string), value_(new string_t(std::move(s))) : final_type_(0), type_(value_t::string), value_(new string_t(std::move(s)))
{} {}
json::json(const char* s) json::json(const char* s)
: type_(value_t::string), value_(new string_t(s)) : final_type_(0), type_(value_t::string), value_(new string_t(s))
{} {}
json::json(const bool b) noexcept json::json(const bool b) noexcept
: type_(value_t::boolean), value_(b) : final_type_(0), type_(value_t::boolean), value_(b)
{} {}
json::json(const array_t& a) json::json(const array_t& a)
: type_(value_t::array), value_(new array_t(a)) : final_type_(0), type_(value_t::array), value_(new array_t(a))
{} {}
json::json(array_t&& a) json::json(array_t&& a)
: type_(value_t::array), value_(new array_t(std::move(a))) : final_type_(0), type_(value_t::array), value_(new array_t(std::move(a)))
{} {}
json::json(const object_t& o) json::json(const object_t& o)
: type_(value_t::object), value_(new object_t(o)) : final_type_(0), type_(value_t::object), value_(new object_t(o))
{} {}
json::json(object_t&& o) json::json(object_t&& o)
: type_(value_t::object), value_(new object_t(std::move(o))) : final_type_(0), type_(value_t::object), value_(new object_t(std::move(o)))
{} {}
/*! /*!
@ -141,33 +144,90 @@ as is to create an array.
@bug With the described approach, we would fail to recognize an array whose @bug With the described approach, we would fail to recognize an array whose
first element is again an arrays as array. first element is again an arrays as array.
@param a an initializer list to create from
@param type_deduction whether the type (array/object) shall eb deducted
@param manual_type if type deduction is switched of, pass a manual type
*/ */
json::json(list_init_t a) json::json(list_init_t a, bool type_deduction, value_t manual_type) : final_type_(0)
{ {
// the initializer list could describe an object
bool is_object = true;
// check if each element is an array with two elements whose first element // check if each element is an array with two elements whose first element
// is a string // is a string
for (const auto& element : a) for (const auto& element : a)
{ {
if (element.type_ != value_t::array or if ((element.final_type_ == 1 and element.type_ == value_t::array)
element.size() != 2 or or (element.type_ != value_t::array or element.size() != 2 or element[0].type_ != value_t::string))
element[0].type_ != value_t::string)
{ {
// we found an element that makes it impossible to use the
// the initializer list describes an array // initializer list as object
type_ = value_t::array; is_object = false;
value_ = new array_t(a); break;
return;
} }
} }
// the initializer list is a list of pairs // adjust type if type deduction is not wanted
type_ = value_t::object; if (not type_deduction)
value_ = new object_t();
for (const json& element : a)
{ {
const std::string k = element[0]; // mark this object's type as final
value_.object->emplace(std::make_pair(std::move(k), final_type_ = 1;
std::move(element[1])));
// if array is wanted, do not create an object though possible
if (manual_type == value_t::array)
{
is_object = false;
}
// if object is wanted but impossible, throw an exception
if (manual_type == value_t::object and not is_object)
{
throw std::logic_error("cannot create JSON object");
}
}
if (is_object)
{
// the initializer list is a list of pairs -> create object
type_ = value_t::object;
value_ = new object_t();
for (auto& element : a)
{
value_.object->emplace(std::make_pair(std::move(element[0]), std::move(element[1])));
}
}
else
{
// the initializer list describes an array -> create array
type_ = value_t::array;
value_ = new array_t(std::move(a));
}
}
/*!
@param a initializer list to create an array from
@return array
*/
json json::array(list_init_t a)
{
return json(a, false, value_t::array);
}
/*!
@param a initializer list to create an object from
@return object
*/
json json::object(list_init_t a)
{
// if more than one element is in the initializer list, wrap it
if (a.size() > 1)
{
return json({a}, false, value_t::object);
}
else
{
return json(a, false, value_t::object);
} }
} }

View file

@ -140,7 +140,7 @@ class json
/// create an object according to given type /// create an object according to given type
json(const value_t); json(const value_t);
/// create a null object /// create a null object
json() = default; json() noexcept;
/// create a null object /// create a null object
json(std::nullptr_t) noexcept; json(std::nullptr_t) noexcept;
/// create a string object from a C++ string /// create a string object from a C++ string
@ -160,7 +160,7 @@ class json
/// create an object (move) /// create an object (move)
json(object_t&&); json(object_t&&);
/// create from an initializer list (to an array or object) /// create from an initializer list (to an array or object)
json(list_init_t); json(list_init_t, bool = true, value_t = value_t::array);
/*! /*!
@brief create a number object (integer) @brief create a number object (integer)
@ -171,7 +171,7 @@ class json
std::numeric_limits<T>::is_integer, T>::type std::numeric_limits<T>::is_integer, T>::type
= 0> = 0>
json(const T n) noexcept json(const T n) noexcept
: type_(value_t::number), : final_type_(0), type_(value_t::number),
value_(static_cast<number_t>(n)) value_(static_cast<number_t>(n))
{} {}
@ -184,7 +184,7 @@ class json
std::is_floating_point<T>::value>::type std::is_floating_point<T>::value>::type
> >
json(const T n) noexcept json(const T n) noexcept
: type_(value_t::number_float), : final_type_(0), type_(value_t::number_float),
value_(static_cast<number_float_t>(n)) value_(static_cast<number_float_t>(n))
{} {}
@ -229,6 +229,11 @@ class json
/// destructor /// destructor
~json() noexcept; ~json() noexcept;
/// explicit keyword to force array creation
static json array(list_init_t = list_init_t());
/// explicit keyword to force object creation
static json object(list_init_t = list_init_t());
/// create from string representation /// create from string representation
static json parse(const std::string&); static json parse(const std::string&);
/// create from string representation /// create from string representation
@ -404,9 +409,10 @@ class json
const_reverse_iterator crend() const noexcept; const_reverse_iterator crend() const noexcept;
private: private:
/// whether the type is final
unsigned final_type_ : 1;
/// the type of this object /// the type of this object
value_t type_ = value_t::null; value_t type_ = value_t::null;
/// the payload /// the payload
value value_ {}; value value_ {};

View file

@ -304,13 +304,70 @@ TEST_CASE("array")
CHECK(v == vec[static_cast<size_t>(v)]); CHECK(v == vec[static_cast<size_t>(v)]);
} }
} }
}
SECTION("Initializer lists")
{
// edge case: This should be an array with two elements which are in // edge case: This should be an array with two elements which are in
// turn arrays with two strings. However, this is treated like the // turn arrays with two strings. However, this is treated like the
// initializer list of an object. // initializer list of an object.
json j_should_be_an_array = { {"foo", "bar"}, {"baz", "bat"} }; json j_should_be_an_array = { {"foo", "bar"}, {"baz", "bat"} };
CHECK(j_should_be_an_array.type() == json::value_t::object); CHECK(j_should_be_an_array.type() == json::value_t::object);
json j_is_an_array = { json::array({"foo", "bar"}), {"baz", "bat"} };
CHECK(j_is_an_array.type() == json::value_t::array);
// array outside initializer list
json j_pair_array = json::array({"s1", "s2"});
CHECK(j_pair_array.type() == json::value_t::array);
// array inside initializer list
json j_pair_array2 = {json::array({"us1", "us2"})};
CHECK(j_pair_array2.type() == json::value_t::array);
// array() is []
json j_empty_array_explicit = json::array();
CHECK(j_empty_array_explicit.type() == json::value_t::array);
// object() is []
json j_empty_object_explicit = json::object();
CHECK(j_empty_object_explicit.type() == json::value_t::object);
// {object({"s1", "s2"})} is [{"s1": "s2"}]
json j_explicit_object = {json::object({"s1", "s2"})};
CHECK(j_explicit_object.type() == json::value_t::array);
// object({"s1", "s2"}) is [{"s1": "s2"}]
json j_explicit_object2 = json::object({"s1", "s2"});
CHECK(j_explicit_object2.type() == json::value_t::object);
// object({{"s1", "s2"}}) is [{"s1": "s2"}]
json j_explicit_object3 = json::object({{"s1", "s2"}});
CHECK(j_explicit_object3.type() == json::value_t::object);
// check errors when explicit object creation is demanded
CHECK_THROWS_AS(json::object({"s1"}), std::logic_error);
CHECK_THROWS_AS(json::object({"s1", 1, 3}), std::logic_error);
CHECK_THROWS_AS(json::object({1, "s1"}), std::logic_error);
CHECK_THROWS_AS(json::object({{1, "s1"}}), std::logic_error);
CHECK_THROWS_AS(json::object({{"foo", "bar"}, {1, "s1"}}), std::logic_error);
// {} is null
json j_null = {};
CHECK(j_null.type() == json::value_t::null);
// {{}} is []
json j_empty_array_implicit = {{}};
CHECK(j_empty_array_implicit.type() == json::value_t::array);
// {1} is [1]
json j_singleton_array = {1};
CHECK(j_singleton_array.type() == json::value_t::array);
// test case from issue #8
json j_issue8 = {json::array({"a", "b"}), json::array({"c", "d"})};
CHECK(j_issue8.type() == json::value_t::array);
CHECK(j_issue8.dump() == "[[\"a\",\"b\"],[\"c\",\"d\"]]");
} }
SECTION("Iterators and empty arrays") SECTION("Iterators and empty arrays")