Skip to content

Commit

Permalink
Merge pull request #609 from theodelrieu/develop
Browse files Browse the repository at this point in the history
Add pair support, fix CompatibleObject conversions (fixes #600)
  • Loading branch information
nlohmann authored Jun 7, 2017
2 parents 92ef196 + cea39df commit 52f934c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 4 deletions.
52 changes: 48 additions & 4 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class invalid_iterator : public exception

Exceptions have ids 3xx.

name / id | example massage | description
name / id | example message | description
----------------------------- | --------------- | -------------------------
json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
Expand Down Expand Up @@ -324,7 +324,7 @@ class type_error : public exception

Exceptions have ids 4xx.

name / id | example massage | description
name / id | example message | description
------------------------------- | --------------- | -------------------------
json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
Expand Down Expand Up @@ -355,9 +355,10 @@ class out_of_range : public exception

Exceptions have ids 5xx.

name / id | example massage | description
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
json.exception.other_error.502 | invalid object size for conversion | Some conversions to user-defined types impose constraints on the object size (e.g. std::pair)

@since version 3.0.0
*/
Expand Down Expand Up @@ -865,6 +866,14 @@ void to_json(BasicJsonType& j, T (&arr)[N])
external_constructor<value_t::array>::construct(j, arr);
}

template <typename BasicJsonType, typename CompatibleString, typename T,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
CompatibleString>::value, int> = 0>
void to_json(BasicJsonType& j, std::pair<CompatibleString, T> const& p)
{
j[p.first] = p.second;
}

///////////////
// from_json //
///////////////
Expand Down Expand Up @@ -1037,10 +1046,24 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
using std::begin;
using std::end;
using value_type = typename CompatibleObjectType::value_type;
std::vector<value_type> v;
v.reserve(j.size());
std::transform(
inner_object->begin(), inner_object->end(), std::back_inserter(v),
[](typename BasicJsonType::object_t::value_type const & p)
{
return value_type
{
p.first,
p.second
.template get<typename CompatibleObjectType::mapped_type>()};
});
// we could avoid the assignment, but this might require a for loop, which
// might be less efficient than the container constructor for some
// containers (would it?)
obj = CompatibleObjectType(begin(*inner_object), end(*inner_object));
obj = CompatibleObjectType(std::make_move_iterator(begin(v)),
std::make_move_iterator(end(v)));
}

// overload for arithmetic types, not chosen for basic_json template arguments
Expand Down Expand Up @@ -1086,6 +1109,27 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
}
}

template <typename BasicJsonType, typename CompatibleString, typename T,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
CompatibleString>::value, int> = 0>
void from_json(const BasicJsonType& j, std::pair<CompatibleString, T>& p)
{
if (not j.is_object())
{
JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name()));
}

auto const inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
auto const size = inner_object->size();
if (size != 1)
{
JSON_THROW(other_error::create(502, "conversion to std::pair requires the object to have exactly one field, but it has " + std::to_string(size)));
}
auto const& obj = *inner_object->begin();
// cannot use *inner_object, need to convert both members
p = std::make_pair(obj.first, obj.second.template get<T>());
}

struct to_json_fn
{
private:
Expand Down
48 changes: 48 additions & 0 deletions test/src/unit-constructor1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,31 @@ TEST_CASE("constructors")
CHECK(j == j_reference);
}

SECTION("std::pair<CompatibleString, T>")
{
std::pair<std::string, std::string> p{"first", "second"};
json j(p);

CHECK((j.get<decltype(p)>() == p));

std::pair<std::string, int> p2{"first", 1};
// use char const*
json j2(std::make_pair("first", 1));

CHECK((j2.get<decltype(p2)>() == p2));
}

SECTION("std::map<std::string, std::string> #600")
{
std::map<std::string, std::string> m;
m["a"] = "b";
m["c"] = "d";
m["e"] = "f";

json j(m);
CHECK((j.get<decltype(m)>() == m));
}

SECTION("std::map<const char*, json>")
{
std::map<const char*, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
Expand All @@ -164,6 +189,7 @@ TEST_CASE("constructors")
CHECK(j == j_reference);
}


SECTION("std::multimap<json::string_t, json>")
{
std::multimap<json::string_t, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
Expand Down Expand Up @@ -954,6 +980,28 @@ TEST_CASE("constructors")
"[json.exception.type_error.301] cannot create object from initializer list");
}

SECTION("std::pair<CompatibleString, T> with error")
{
SECTION("wrong field number")
{
json j{{"too", "much"}, {"string", "fields"}};
CHECK_THROWS_AS((j.get<std::pair<std::string, std::string>>()), json::other_error);
CHECK_THROWS_WITH((j.get<std::pair<std::string, std::string>>()),
"[json.exception.other_error.502] conversion "
"to std::pair requires the object to have "
"exactly one field, but it has 2");
}

SECTION("wrong JSON type")
{
json j(42);
CHECK_THROWS_AS((j.get<std::pair<std::string, std::string>>()), json::type_error);
CHECK_THROWS_WITH((j.get<std::pair<std::string, std::string>>()),
"[json.exception.type_error.302] type must be object, but is number");
}
}


SECTION("empty array")
{
json j = json::array();
Expand Down

0 comments on commit 52f934c

Please sign in to comment.