#include <jsoncons/reflect/json_conv_traits.hpp>json_conv_traits defines a compile time template based interface for conversion between a basic_json value
and a value of some other type. json_conv_traits implementations must specialize a traits class for a type T:
template <typename Json,typename T,typename Enable=void>
struct json_conv_traits
{
static constexpr bool is(const Json& j) noexcept;
template<typename Alloc,typename TempAlloc>
static conversion_result<T> try_as(const allocator_set<Alloc,TempAlloc>&, const Json& j);
template <typename Alloc, typename TempAlloc>
static Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const T& val);
};The function
json_conv_traits<Json,T>::is(const Json& j) noexcept
indictates whether j satisfies the requirements of type T. This function supports
the type selection strategy when converting a Json value to the proper derived class
in the polymorphic case, and when converting a Json value to the proper alternative
type in the variant case.
The function
template <typename Alloc, typename TempAlloc>
conversion_result<T> try_as(const allocator_set<Alloc,TempAlloc>& aset, const Json& j)
tries to convert j to a value of type T and returns an std::expected-like result (like std::expected<T,jsoncons::conversion_error>.)
The function
template <typename Alloc, typename TempAlloc>
Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const T& val)
tries to convert val into a Json value.
jsoncons includes specializiations for most types in the standard library. And it includes convenience
macros that make specializing json_conv_traits for your own types easier.
allocator_set
conversion_result
#include <jsoncons/json.hpp>
#include <string>
#include <optional>
#include <iostream>
#include <cassert>
namespace ns {
struct OptionalExample {
std::string text;
std::optional<std::string> optional;
std::optional<std::string> optional_skip;
explicit OptionalExample() = default;
};
} // namespace ns
namespace jsoncons {
namespace reflect {
template <typename Json>
struct json_conv_traits<Json, ns::OptionalExample>
{
using value_type = ns::OptionalExample;
static bool is(const Json& j) noexcept
{
return j.is_object() && j.contains("text") && j.contains("optional");
}
template <typename Alloc, typename TempAlloc>
static conversion_result<value_type> try_as(const allocator_set<Alloc,TempAlloc>&, const Json& j)
{
using result_type = conversion_result<value_type>;
if (!j.is_object())
{
return result_type(jsoncons::unexpect, conv_errc::not_map, "ns::OptionalExample");
}
value_type val;
{
auto it = j.find("text");
if (it == j.object_range().end())
{
return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "text");
}
auto r = it->value().template try_as<std::string>();
if (!r)
{
return result_type(unexpect, r.error().code());
}
val.text = std::move(*r);
}
{
auto it = j.find("optional");
if (it != j.object_range().end())
{
auto r = it->value().template try_as<std::string>();
if (!r)
{
return result_type(unexpect, r.error().code());
}
val.optional = std::move(*r);
}
}
{
auto it = j.find("optional_skip");
if (it != j.object_range().end())
{
auto r = it->value().template try_as<std::string>();
if (!r)
{
return result_type(unexpect, r.error().code());
}
val.optional_skip = std::move(*r);
}
}
return result_type(std::move(val));
}
template <typename Alloc, typename TempAlloc>
static Json to_json(const allocator_set<Alloc,TempAlloc>& aset, const ns::OptionalExample& val)
{
Json j = jsoncons::make_obj_using_allocator<Json>(aset.get_allocator(), json_object_arg);
j.try_emplace("text", val.text);
if (val.optional)
{
j.try_emplace("optional", *val.optional);
}
if (val.optional_skip)
{
j.try_emplace("optional_skip", *val.optional_skip);
}
return j;
}
};
} // namespace reflect
} // namespace jsoncons
int main()
{
std::error_code ec;
ns::OptionalExample val;
val.text = "Sample!";
val.optional = "Available Value";
std::string buffer;
auto wresult = jsoncons::try_encode_json_pretty(val, buffer);
if (!wresult)
{
std::cout << wresult.error().message() << "\n";
exit(1);
}
std::cout << buffer << "\n";
}Output:
{
"text": "Sample!",
"optional": "Available Value"
}-
If you're using C++20 or higher, you can use std::make_obj_using_allocator in
to_jsoninstead ofjsoncons::make_obj_using_allocator. -
To save typing and enhance readability, you can use the convenience macro
JSONCONS_N_MEMBER_TRAITSto generate the traits classes,
JSONCONS_N_MEMBER_TRAITS(OptionalExample, 1, text, optional, optional_skip);
#include <jsoncons/json.hpp>
#include <../examples/src/common/mock_stateful_allocator.hpp>
#include <scoped_allocator>
#include <vector>
#include <string>
#include <iostream>
#include <cassert>
namespace ns {
template <typename Alloc>
struct book
{
using allocator_type = Alloc;
using char_allocator_type = typename std::allocator_traits<Alloc>:: template rebind_alloc<char>;
using string_type = std::basic_string<char, std::char_traits<char>, char_allocator_type>;
string_type author;
string_type title;
double price{0};
book(const Alloc& alloc)
: author(alloc), title(alloc)
{
}
book(book&& other, const Alloc& alloc)
: author(std::move(other.author), alloc), title(std::move(other.title), alloc)
{
}
};
} // namespace ns
namespace jsoncons {
namespace reflect {
template <typename Json, typename Alloc>
struct json_conv_traits<Json, ns::book<Alloc>>
{
using value_type = ns::book<Alloc>;
static bool is(const Json& j) noexcept
{
return j.is_object() && j.contains("author") &&
j.contains("title") && j.contains("price");
}
template <typename Alloc, typename TempAlloc>
static conversion_result<value_type> try_as(const allocator_set<Alloc, TempAlloc>& aset, const Json& j)
{
using result_type = conversion_result<value_type>;
if (!j.is_object())
{
return result_type(jsoncons::unexpect, conv_errc::not_map, "ns::book");
}
auto val = jsoncons::make_obj_using_allocator<value_type>(aset.get_allocator());
{
auto it = j.find("author");
if (it == j.object_range().end())
{
return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "author");
}
auto r = it->value().template try_as<typename value_type::string_type>(aset);
if (!r)
{
return result_type(unexpect, r.error().code());
}
val.author = std::move(*r);
}
{
auto it = j.find("title");
if (it == j.object_range().end())
{
return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "title");
}
auto r = it->value().template try_as<typename value_type::string_type>(aset);
if (!r)
{
return result_type(jsoncons::unexpect, r.error().code());
}
val.title = std::move(*r);
}
{
auto it = j.find("price");
if (it == j.object_range().end())
{
return result_type(jsoncons::unexpect, conv_errc::missing_required_member, "price");
}
auto r = it->value().template try_as<double>(aset);
if (!r)
{
return result_type(jsoncons::unexpect, r.error().code(), "price");
}
val.price = *r;
}
return result_type(std::move(val));
}
template <typename Alloc, typename TempAlloc>
static Json to_json(const allocator_set<Alloc, TempAlloc>& aset, const value_type& val)
{
auto j = jsoncons::make_obj_using_allocator<Json>(aset.get_allocator(), json_object_arg);
j.try_emplace("author", val.author);
j.try_emplace("title", val.title);
j.try_emplace("price", val.price);
return j;
}
};
} // namespace reflect
} // namespace jsoncons
template <typename T>
using cust_allocator = std::scoped_allocator_adaptor<mock_stateful_allocator<T>>;
using book_type = ns::book<cust_allocator<char>>;
using books_type = std::vector<book_type, cust_allocator<book_type>>;
int main()
{
const std::string input = R"(
[
{
"author" : "Haruki Murakami",
"title" : "Kafka on the Shore",
"price" : 25.17
},
{
"author" : "Charles Bukowski",
"title" : "Pulp",
"price" : 22.48
}
]
)";
cust_allocator<book_type> alloc(1);
auto aset = jsoncons::make_alloc_set(alloc);
auto rin = jsoncons::try_decode_json<books_type>(aset, input);
assert(rin);
std::string output;
auto rout = jsoncons::try_encode_json_pretty(aset, *rin, output);
assert(rout);
std::cout << output << "\n";
}Output:
[
{
"author": "Haruki Murakami",
"title": "Kafka on the Shore",
"price": 0.0
},
{
"author": "Charles Bukowski",
"title": "Pulp",
"price": 0.0
}
]
-
If you're using C++20 or higher, you can use std::make_obj_using_allocator in
try_asandto_jsoninstead ofjsoncons::make_obj_using_allocator. -
To save typing and enhance readability, you can use the convenience macro
JSONCONS_TMPL_ALL_MEMBER_TRAITSto generate the traits classes,
JSONCONS_TMPL_ALL_MEMBER_TRAITS(1, ns::book, author, title, price)