Skip to content

Commit 52f934c

Browse files
authored
Merge pull request #609 from theodelrieu/develop
Add pair support, fix CompatibleObject conversions (fixes #600)
2 parents 92ef196 + cea39df commit 52f934c

2 files changed

Lines changed: 96 additions & 4 deletions

File tree

src/json.hpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class invalid_iterator : public exception
285285
286286
Exceptions have ids 3xx.
287287
288-
name / id | example massage | description
288+
name / id | example message | description
289289
----------------------------- | --------------- | -------------------------
290290
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.
291291
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.
@@ -324,7 +324,7 @@ class type_error : public exception
324324
325325
Exceptions have ids 4xx.
326326
327-
name / id | example massage | description
327+
name / id | example message | description
328328
------------------------------- | --------------- | -------------------------
329329
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.
330330
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.
@@ -355,9 +355,10 @@ class out_of_range : public exception
355355
356356
Exceptions have ids 5xx.
357357
358-
name / id | example massage | description
358+
name / id | example message | description
359359
------------------------------ | --------------- | -------------------------
360360
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
361+
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)
361362
362363
@since version 3.0.0
363364
*/
@@ -865,6 +866,14 @@ void to_json(BasicJsonType& j, T (&arr)[N])
865866
external_constructor<value_t::array>::construct(j, arr);
866867
}
867868

869+
template <typename BasicJsonType, typename CompatibleString, typename T,
870+
enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
871+
CompatibleString>::value, int> = 0>
872+
void to_json(BasicJsonType& j, std::pair<CompatibleString, T> const& p)
873+
{
874+
j[p.first] = p.second;
875+
}
876+
868877
///////////////
869878
// from_json //
870879
///////////////
@@ -1037,10 +1046,24 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
10371046
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
10381047
using std::begin;
10391048
using std::end;
1049+
using value_type = typename CompatibleObjectType::value_type;
1050+
std::vector<value_type> v;
1051+
v.reserve(j.size());
1052+
std::transform(
1053+
inner_object->begin(), inner_object->end(), std::back_inserter(v),
1054+
[](typename BasicJsonType::object_t::value_type const & p)
1055+
{
1056+
return value_type
1057+
{
1058+
p.first,
1059+
p.second
1060+
.template get<typename CompatibleObjectType::mapped_type>()};
1061+
});
10401062
// we could avoid the assignment, but this might require a for loop, which
10411063
// might be less efficient than the container constructor for some
10421064
// containers (would it?)
1043-
obj = CompatibleObjectType(begin(*inner_object), end(*inner_object));
1065+
obj = CompatibleObjectType(std::make_move_iterator(begin(v)),
1066+
std::make_move_iterator(end(v)));
10441067
}
10451068

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

1112+
template <typename BasicJsonType, typename CompatibleString, typename T,
1113+
enable_if_t<std::is_constructible<typename BasicJsonType::string_t,
1114+
CompatibleString>::value, int> = 0>
1115+
void from_json(const BasicJsonType& j, std::pair<CompatibleString, T>& p)
1116+
{
1117+
if (not j.is_object())
1118+
{
1119+
JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name()));
1120+
}
1121+
1122+
auto const inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
1123+
auto const size = inner_object->size();
1124+
if (size != 1)
1125+
{
1126+
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)));
1127+
}
1128+
auto const& obj = *inner_object->begin();
1129+
// cannot use *inner_object, need to convert both members
1130+
p = std::make_pair(obj.first, obj.second.template get<T>());
1131+
}
1132+
10891133
struct to_json_fn
10901134
{
10911135
private:

test/src/unit-constructor1.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,31 @@ TEST_CASE("constructors")
156156
CHECK(j == j_reference);
157157
}
158158

159+
SECTION("std::pair<CompatibleString, T>")
160+
{
161+
std::pair<std::string, std::string> p{"first", "second"};
162+
json j(p);
163+
164+
CHECK((j.get<decltype(p)>() == p));
165+
166+
std::pair<std::string, int> p2{"first", 1};
167+
// use char const*
168+
json j2(std::make_pair("first", 1));
169+
170+
CHECK((j2.get<decltype(p2)>() == p2));
171+
}
172+
173+
SECTION("std::map<std::string, std::string> #600")
174+
{
175+
std::map<std::string, std::string> m;
176+
m["a"] = "b";
177+
m["c"] = "d";
178+
m["e"] = "f";
179+
180+
json j(m);
181+
CHECK((j.get<decltype(m)>() == m));
182+
}
183+
159184
SECTION("std::map<const char*, json>")
160185
{
161186
std::map<const char*, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
@@ -164,6 +189,7 @@ TEST_CASE("constructors")
164189
CHECK(j == j_reference);
165190
}
166191

192+
167193
SECTION("std::multimap<json::string_t, json>")
168194
{
169195
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()}};
@@ -954,6 +980,28 @@ TEST_CASE("constructors")
954980
"[json.exception.type_error.301] cannot create object from initializer list");
955981
}
956982

983+
SECTION("std::pair<CompatibleString, T> with error")
984+
{
985+
SECTION("wrong field number")
986+
{
987+
json j{{"too", "much"}, {"string", "fields"}};
988+
CHECK_THROWS_AS((j.get<std::pair<std::string, std::string>>()), json::other_error);
989+
CHECK_THROWS_WITH((j.get<std::pair<std::string, std::string>>()),
990+
"[json.exception.other_error.502] conversion "
991+
"to std::pair requires the object to have "
992+
"exactly one field, but it has 2");
993+
}
994+
995+
SECTION("wrong JSON type")
996+
{
997+
json j(42);
998+
CHECK_THROWS_AS((j.get<std::pair<std::string, std::string>>()), json::type_error);
999+
CHECK_THROWS_WITH((j.get<std::pair<std::string, std::string>>()),
1000+
"[json.exception.type_error.302] type must be object, but is number");
1001+
}
1002+
}
1003+
1004+
9571005
SECTION("empty array")
9581006
{
9591007
json j = json::array();

0 commit comments

Comments
 (0)