Boost Json conversion error for array of custom object

hirish

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?

sehe

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)}};

Live On Coliru

#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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related