I'm trying to code a simple json to struct (and back) conversion by using tag_invoke
overload of boost::json
lib.
Those are my structs:
template<class T>
void extract( boost::json::object const& obj, T& t, boost::json::string_view key )
{
t = boost::json::value_to<T>( obj.at( key ) );
};
struct CRDApp {
std::string type;
std::string image;
uint32_t replicas;
friend CRDApp tag_invoke( boost::json::value_to_tag<CRDApp>, boost::json::value const& jv )
{
CRDApp app;
boost::json::object const& obj = jv.as_object();
extract( obj, app.type, "type" );
extract( obj, app.image, "image" );
extract( obj, app.replicas, "replicas" );
return app;
}
friend void tag_invoke( boost::json::value_from_tag, boost::json::value& jv, CRDApp const& app )
{
jv = {
{ "type" , app.type },
{ "image", app.image },
{ "replicas", app.replicas }
};
}
};
struct CRDSpec {
std::string _namespace;
std::vector<CRDApp> apps;
friend CRDSpec tag_invoke( boost::json::value_to_tag<CRDSpec>, boost::json::value const& jv )
{
CRDSpec spec;
boost::json::object const& obj = jv.as_object();
extract( obj, spec._namespace, "namespace" );
extract( obj, spec.apps, "apps" );
return spec;
}
friend void tag_invoke( boost::json::value_from_tag, boost::json::value& jv, CRDSpec const& spec )
{
jv = {
{ "namespace" , spec._namespace },
{ "apps", spec.apps }
};
}
};
I've tested the json to struct conversion and is working fine, but once I've added the tag_invoke
in order to convert from struct to json, the code is not compiling anymore with error:
error: no matching function for call to 'boost::json::value::value(const std::vector<CRDApp>&, std::remove_reference<boost::json::storage_ptr&>::type)'
35 | return value(
| ^~~~~~
36 | *reinterpret_cast<
| ~~~~~~~~~~~~~~~~~~
37 | T const*>(p),
| ~~~~~~~~~~~~~
38 | std::move(sp));
| ~~~~~~~~~~~~~~
If I comment out the { "apps", spec.apps }
line, the code compile again. Docs say it should automatically handle standard containers like std::vector
.
Am I missing something?
The whole idea of the tag_invoke customization is not that you get "magic" or implicit conversions. You have to call it:
jv = {{"namespace", spec._namespace}, {"apps", boost::json::value_from(spec.apps)}};
#include <boost/json/src.hpp>
#include <iostream>
template <class T> void extract(boost::json::object const& obj, T& v, boost::json::string_view key) {
v = boost::json::value_to<T>(obj.at(key));
};
struct CRDApp {
std::string type;
std::string image;
uint32_t replicas;
friend CRDApp tag_invoke(boost::json::value_to_tag<CRDApp>, boost::json::value const& jv) {
CRDApp app;
boost::json::object const& obj = jv.as_object();
extract(obj, app.type, "type");
extract(obj, app.image, "image");
extract(obj, app.replicas, "replicas");
return app;
}
friend void tag_invoke(boost::json::value_from_tag, boost::json::value& jv, CRDApp const& app) {
jv = {{"type", app.type}, {"image", app.image}, {"replicas", app.replicas}};
}
auto operator<=>(CRDApp const&) const = default;
};
struct CRDSpec {
std::string _namespace;
std::vector<CRDApp> apps;
friend CRDSpec tag_invoke(boost::json::value_to_tag<CRDSpec>, boost::json::value const& jv) {
CRDSpec spec;
boost::json::object const& obj = jv.as_object();
extract(obj, spec._namespace, "namespace");
extract(obj, spec.apps, "apps");
return spec;
}
friend void tag_invoke(boost::json::value_from_tag, boost::json::value& jv, CRDSpec const& spec) {
jv = {{"namespace", spec._namespace}, {"apps", boost::json::value_from(spec.apps)}};
}
auto operator<=>(CRDSpec const&) const = default;
};
int main() {
CRDSpec const spec{"some_ns",
{
{"type1", "image1", 111},
{"type2", "image2", 222},
{"type3", "image3", 333},
{"type4", "image4", 444},
{"type5", "image5", 555},
{"type6", "image6", 666},
{"type7", "image7", 777},
{"type8", "image8", 888},
{"type9", "image9", 999},
}};
auto js = boost::json::value_from(spec);
std::cout << js << "\n";
auto roundtrip = boost::json::value_to<CRDSpec>(js);
std::cout << "Roundtrip " << (roundtrip == spec? "equal":"different") << "\n";
}
Prints
{"namespace":"some_ns","apps":[{"type":"type1","image":"image1","replicas":111},{"type":"type2","image":"image2","replicas":222},{"type":"type3","image":"image3","replicas":333},{"type":"type4","image":"image4","replicas":444},{"type":"type5","image":"image5","replicas":555},{"type":"type6","image":"image6","replicas":666},{"type":"type7","image":"image7","replicas":777},{"type":"type8","image":"image8","replicas":888},{"type":"type9","image":"image9","replicas":999}]}
Roundtrip equal