Info
-
Did you know that the JSON standard does not specify that the insertion order of object elements should be preserved?
Example
int main() {
{
nlohmann::json json{};
json["value"] = 42;
json["array"] = std::array{1, 2, 3};
std::cout << json.dump(); // prints {"array":[1,2,3],"value":42}
}
{
nlohmann::ordered_json json{};
json["value"] = 42;
json["array"] = std::array{1, 2, 3};
std::cout << json.dump(); // prints {"value":42", array":[1,2,3]}
}
}
Puzzle
- Can you extend
to_json
function from the last week with support of insertion_order/alphabetical_order policies?
template<class TPolicy = class insertion_order>
constexpr auto to_json(const auto& input); // TODO
template <class T>
struct named {
std::string_view name{};
T value{};
};
int main() {
using namespace boost::ut;
using std::literals::string_literals::operator""s;
"to json"_test = [] {
const auto t = std::tuple{
named{.name = "int", .value = 1},
named{.name = "double", .value = 2.0},
named{.name = "array", .value = std::array{1, 2, 3}},
named{.name = "compound",
.value = std::tuple{named{.name = "unsigned", .value = 42u}}}};
"default order"_test = [=] {
const auto json = to_json(t);
expect(R"({"int":1,"double":2.0,"array":[1,2,3],"compound":{"unsigned":42}})"s == json.dump());
};
"insertion order"_test = [=] {
const auto json = to_json<class insertion_order>(t);
expect(R"({"int":1,"double":2.0,"array":[1,2,3],"compound":{"unsigned":42}})"s == json.dump());
};
"alphabetical order"_test = [=] {
const auto json = to_json<class alphabetical_order>(t);
expect(R"({"array":[1,2,3],"compound":{"unsigned":42},"double":2.0,"int":1})"s == json.dump());
};
};
}
Solutions
struct insertion_order {
using json_t = nlohmann::ordered_json;
};
struct alphabetical_order {
using json_t = nlohmann::json;
};
using default_order = insertion_order;
template <class TPolicy = default_order>
constexpr auto& to_json(const auto& t) {
return t;
}
template <class TPolicy = default_order, typename... Ts>
constexpr auto to_json(const std::tuple<Ts...>& t) {
return std::apply([] (const auto&... ts) {
typename TPolicy::json_t obj{};
((obj[std::string{ts.name}] = to_json<TPolicy>(ts.value)), ...);
return obj;
}, t);
}
struct alphabetical_order {
using json_type = nlohmann::json;
};
struct insertion_order {
using json_type = nlohmann::ordered_json;
};
template <typename TPolicy = insertion_order>
constexpr auto to_json(auto const& value) -> decltype(auto) {
return value;
}
template <typename TPolicy = insertion_order, typename... Ts>
constexpr auto to_json(std::tuple<Ts...> const& tuple) {
typename TPolicy::json_type json{};
std::apply([&] (auto const&... named) {
((json[std::string{named.name}] = to_json<TPolicy>(named.value)), ...);
}, tuple);
return json;
}
struct alphabetical_order {
using json_type = nlohmann::json;
};
struct insertion_order {
using json_type = nlohmann::ordered_json;
};
template <typename TPolicy = insertion_order>
constexpr auto to_json(auto const& value) -> decltype(auto) {
return value;
}
template <typename TPolicy = insertion_order, typename... Ts>
constexpr auto to_json(std::tuple<Ts...> const& tuple) {
typename TPolicy::json_type json{};
std::apply([&] (auto const&... named) {
((json[std::string{named.name}] = to_json<TPolicy>(named.value)), ...);
}, tuple);
return json;
}
class insertion_order;
class alphabetical_order;
template<class T>
using json_t = typename std::conditional_t<
std::is_same_v<T, insertion_order>,
std::type_identity<nlohmann::ordered_json>,
std::enable_if<
std::is_same_v<T, alphabetical_order>,
nlohmann::json>>::type;
template<class TPolicy = class insertion_order, class... Ts>
constexpr auto to_json(const std::tuple<named<Ts>...>& t) {
return std::apply([] (const auto&... ts) {
json_t<TPolicy> json{};
(..., (json[std::string{ts.name}] = to_json<TPolicy>(ts.value)));
return json;
}, t);
}
constexpr auto& to_json(const auto& t) {
return t;
}
template <class TPolicy = class insertion_order, typename... Ts>
constexpr auto to_json(const std::tuple<Ts...>& t) {
return std::apply([] (const auto&... ts) {
if constexpr( std::is_same_v<TPolicy, class insertion_order> ) {
nlohmann::ordered_json obj{};
((obj[std::string{ts.name}] = to_json(ts.value)), ...);
return obj;
} else {
nlohmann::json obj{};
((obj[std::string{ts.name}] = to_json(ts.value)), ...);
return obj;
}
}, t);
}
constexpr auto to_json(const auto & value) {
return value;
}
template<class ...T> constexpr auto to_json(const std::tuple<named<T>...>& tp) {
nlohmann::ordered_json json{};
std::apply([&](const auto&... n)
{
((json[std::string{n.name}] = to_json(n.value)), ...);
}, tp);
return json;
};
template<class ...T> constexpr auto to_alphabetical_json(const std::tuple<named<T>...>& tp) {
nlohmann::json json{};
std::apply([&](const auto&... n)
{
((json[std::string{n.name}] = to_json(n.value)), ...);
}, tp);
return json;
};
template<class TPolicy = insertion_order>
constexpr auto to_json(const auto& input)
{
if constexpr(std::is_same_v<TPolicy, insertion_order>) {
return to_json(input);
}
else if constexpr(std::is_same_v<TPolicy, alphabetical_order>) {
return to_alphabetical_json(input);
}
};
template<class TPolicy = class insertion_order>
constexpr auto to_json( auto const & arg){ return arg; }
template<class TPolicy = class insertion_order,class T>
constexpr std::tuple<const char*,decltype(to_json(T{}))> to_json( named<T> const & arg)
{
return {arg.name.data(),to_json(arg.value)};
}
template<class TPolicy = class insertion_order,class ... T>
constexpr auto to_json(std::tuple< named<T> ... > const & arg)
{
if constexpr ( std::is_same_v<TPolicy,class insertion_order> )
return std::apply( []( auto const & ...args ){ return nlohmann::ordered_json{to_json(args)...}; },arg);
else
return std::apply( []( auto const & ...args ){ return nlohmann::json{to_json(args)...}; },arg);
}