diff --git a/src/json.hpp b/src/json.hpp index a58d38a7c..e6bf9a404 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -91,6 +91,14 @@ SOFTWARE. */ namespace nlohmann { +// TODO add real documentation before PR + +// Traits structure declaration, users can specialize it for their own types +// +// constructing a json object from a user-defined type will call the +// 'json to_json(T)' function +// +// whereas calling json::get will call 'T from_json(json const&)' template struct json_traits; @@ -125,6 +133,7 @@ struct has_mapped_type }; // taken from http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae +// used to determine if json_traits is defined for a given type T template struct has_destructor { @@ -184,6 +193,7 @@ struct DecimalSeparator : std::numpunct } // taken from ranges-v3 +// TODO add doc template struct __static_const { @@ -1280,6 +1290,20 @@ class basic_json assert_invariant(); } + // constructor chosen if json_traits is specialized for type T + // note: constructor is marked explicit to avoid the following issue: + // + // struct not_equality_comparable{}; + // + // not_equality_comparable{} == not_equality_comparable{}; + // + // this will construct implicitely 2 json objects and call operator== on them + // which can cause nasty bugs on the user's in json-unrelated code + // + // the trade-off is expressivety in initializer-lists + // auto j = json{{"a", json(not_equality_comparable{})}}; + // + // we can remove this constraint though, since lots of ctor are not explicit already template < typename T, typename = @@ -2647,10 +2671,13 @@ class basic_json // value access // ////////////////// + // get_impl overload chosen if json_traits struct is specialized for type T + // simply returns json_traits::from_json(*this); + // TODO add alias templates (enable_if_t etc) template < typename T, - typename = - typename std::enable_if::type>::type>::value>::type> auto get_impl(T *) const -> decltype( json_traits::type>::type>::from_json(*this); } + // this one is quite atrocious + // this overload is chosen ONLY if json_traits struct is not specialized, and if the expression nlohmann::from_json(*this, T&) is valid + // I chose to prefer the json_traits specialization if it exists, since it's a more advanced use. + // But we can of course change this behaviour + template + auto get_impl(T *) const -> typename std::enable_if< + not detail::has_json_traits::type>::value, + typename std::remove_cv(), + std::declval()), + std::declval())>::type>::type>::type + { + typename std::remove_cv::type>::type + ret; + ::nlohmann::from_json(*this, ret); + return ret; + } + /// get an object (explicit) template _val; }; +struct no_json_traits_type +{ + int a; +}; + // free to/from_json functions json to_json(empty_type) @@ -86,6 +91,22 @@ json to_json(bit_more_complex_type const& p) return json{{"a", to_json(p.a)}, {"b", to_json(p.b)}, {"c", p.c}}; } +template +json to_json(optional_type const& opt) +{ + using nlohmann::to_json; + if (!opt) + return nullptr; + return to_json(*opt); +} + +json to_json(no_json_traits_type const& p) +{ + json ret; + ret["a"] = p.a; + return ret; +} + void from_json(json const&j, empty_type& t) { assert(j.empty()); @@ -104,6 +125,11 @@ void from_json(json const&j, bit_more_complex_type& t) j["c"].get()}; } +void from_json(json const& j, no_json_traits_type& t) +{ + t.a = j["a"].get(); +} + template void from_json(json const& j, optional_type& t) { @@ -113,15 +139,6 @@ void from_json(json const& j, optional_type& t) t = j.get(); } -template -json to_json(optional_type const& opt) -{ - using nlohmann::to_json; - if (!opt) - return nullptr; - return to_json(*opt); -} - inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept { return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); @@ -141,6 +158,11 @@ inline bool operator==(optional_type const& lhs, optional_type const& rhs) return false; return *lhs == *rhs; } + +inline bool operator==(no_json_traits_type const& lhs, no_json_traits_type const& rhs) +{ + return lhs.a == rhs.a; +} } namespace nlohmann @@ -402,6 +424,16 @@ TEST_CASE("to_json free function", "[udt]") CHECK(expected == j); } } + + SECTION("no json_traits specialization") + { + udt::no_json_traits_type t{42}; + + json expected; + expected["a"] = 42; + auto const j = nlohmann::to_json(t); + CHECK(j == expected); + } } TEST_CASE("from_json free function", "[udt]") @@ -411,7 +443,6 @@ TEST_CASE("from_json free function", "[udt]") auto const expected = udt::pod_type{42, 42, 42}; auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}}; - // i really dislike this output parameter udt::pod_type p; nlohmann::from_json(j, p); CHECK(p == expected); @@ -450,6 +481,18 @@ TEST_CASE("from_json free function", "[udt]") nlohmann::from_json(j, o); CHECK(expected == o); } + } + SECTION("no json_traits specialization") + { + udt::no_json_traits_type expected{42}; + udt::no_json_traits_type res; + json j; + j["a"] = 42; + nlohmann::from_json(j, res); + CHECK(res == expected); + + res = j.get(); + CHECK(res == expected); } }