beached / daw_json_link

Fast, convenient JSON serialization and parsing in C++
https://beached.github.io/daw_json_link/
Boost Software License 1.0
460 stars 30 forks source link

Missing contract for std::variant types #406

Closed mohabouje closed 10 months ago

mohabouje commented 10 months ago

Hi,

I have a stream of data that could hold different types. The JSON objects do not provide a member to identify what type they are holding. Each JSON object has a unique structure, so no conflicts are expected.

In C++, stuff is represented with a std::variant that holds each type. I want to be able to do something like this:

#include <daw/json/daw_json_link.h>

#include <variant>

int main(int argc, char** argv) {
    using type = std::variant<std::monostate, double, std::string_view>;
    type a     = daw::json::from_json<type>(R"("hello")");
}

But It looks like the contract for variant types is missing:

error: /Users/mohabouje/.conan2/p/daw_j39d5fb482a662/p/include/daw/json/impl/daw_json_parse_common.h:802:6: error: static assertion failed due to requirement 'daw::deduced_false_v<std::variant<double, std::string_view>>': Could not deduced data contract type

I've tried to implement the contract myself based on the work already done for json_submember_tagged_variant, but I cannot seem to figure out stuff correctly.

  template <typename... TypeT>
  struct json_data_contract<std::variant<TypeT...>> {
      using type = /* TODO: implement this */;

      inline static auto to_json_data(std::variant<TypeT...> const& data) {
          return std::visit(
              [](auto&& value) { return json_data_contract<std::decay_t<decltype(value)>>::to_json_data(value); },
              data);
      }
  };

A simple solution: the parser will try to parse each member individually and build the variant with the first one that successfully was parsed. If std::monostate is on the list and nothing was parsed correctly, return that case. If std::monostate is not there, throw an error.

beached commented 10 months ago

The following should work https://jsonlink.godbolt.org/z/dbjPWea3M

However, I believe that std::monostate should be mapped in the library and will be adding that to the next revision. That will be a break for code that uses it, but it's an easy fix to remove the mapping too. The issue with a deduced mapping for sum types/variant is there is no correct way to do it but server. So there is no deduction for it. https://github.com/beached/daw_json_link/blob/release/docs/cookbook/variant.md Talks about the ways variants can be mapped though.

#include <daw/json/daw_json_link.h>

#include <variant>

namespace daw::json {
    template<>
    struct json_data_contract<std::monostate> {
        using type = json_member_list<>;

        static constexpr std::tuple<> to_json_data( std::monostate ) noexcept {
            return {};
        }
    };
}

int main(int argc, char** argv) {
    using type = daw::json::json_variant_null_no_name<
        std::variant<std::monostate, double, std::string_view>>;
    auto a = daw::json::from_json<type>(R"("hello")");
}
beached commented 10 months ago

After #407 you should be able to use code like

#include <daw/json/daw_json_link.h>

#include <variant>

int main(int argc, char** argv) {
    using type = daw::json::json_variant_null_no_name<
        std::variant<std::monostate, double, std::string_view>>;
    auto a = daw::json::from_json<type>(R"("hello")");
}
mohabouje commented 10 months ago

Thanks for the quick fix! Everything works as expected 😀

beached commented 10 months ago

Reopening, forgot to handle the serialization part