-
Notifications
You must be signed in to change notification settings - Fork 34
Closed
Labels
Description
OK this is a bizarre issue, following is a minimal example:
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
#include <magic_enum.hpp>
#include <daw/daw_tuple_forward.h>
#include <daw/json/daw_json_link.h>
enum class test_enum : int {
val_0 = 0,
val_1 = 1,
};
enum class data_type : int { FOO = 0, BAR = 1 };
struct foo_type {
int field_1;
};
struct bar_type {
test_enum field_1;
// std::string field_1;
};
using variant_type = std::variant<foo_type, bar_type>;
namespace daw::json {
template <typename E> struct json_data_contract<E, std::enable_if_t<std::is_enum_v<E>>> {
template <typename Enum> struct enum_name_converter {
static_assert(std::is_enum_v<Enum>, "Only enum types are supported");
constexpr std::string_view operator()(Enum e) const { return magic_enum::enum_name(e); }
constexpr Enum operator()(std::string_view sv) const {
auto test = magic_enum::enum_cast<Enum>(sv);
if (!test) {
std::string msg = "Bad enum value ";
msg += sv;
msg += " for enum type ";
msg += magic_enum::enum_type_name<Enum>();
throw std::runtime_error(msg);
}
return test.value();
}
};
using type =
json_type_alias<json_custom_no_name<E, enum_name_converter<E>, enum_name_converter<E>>>;
};
template <> struct json_data_contract<foo_type> {
static constexpr char const dtype[] = "dtype";
static constexpr char const field_1[] = "field_1";
struct builder {
template <class... Args> constexpr auto operator()(data_type dtype, Args &&...args) const {
return foo_type{std::forward<Args>(args)...};
}
};
using constructor_t = builder;
using type = json_member_list<json_class<dtype, data_type>, json_number<field_1, int>>;
static inline auto to_json_data(const foo_type &val) {
return daw::forward_nonrvalue_as_tuple(data_type::FOO, val.field_1);
}
};
template <> struct json_data_contract<bar_type> {
static constexpr char const dtype[] = "dtype";
static constexpr char const field_1[] = "field_1";
struct builder {
template <class... Args> constexpr auto operator()(data_type dtype, Args &&...args) const {
return bar_type{std::forward<Args>(args)...};
}
};
using constructor_t = builder;
using type = json_member_list<json_class<dtype, data_type>, json_class<field_1, test_enum>>;
// using type = json_member_list<json_class<dtype, data_type>, json_string<field_1>>;
static inline auto to_json_data(const bar_type &val) {
return daw::forward_nonrvalue_as_tuple(data_type::BAR, val.field_1);
}
};
template <> struct json_data_contract<variant_type> {
struct switcher {
// Convert JSON tag member to type index
constexpr size_t operator()(data_type type) const { return static_cast<size_t>(type); }
// Get value for Tag from class value
data_type operator()(const variant_type &val) const {
return static_cast<data_type>(val.index());
}
};
static constexpr char const dtype[] = "dtype";
using type =
json_submember_tagged_variant<json_class<dtype, data_type>, switcher, foo_type, bar_type>;
};
} // namespace daw::json
int main(int argc, char **argv) {
std::ifstream t("test.json");
std::stringstream buffer;
buffer << t.rdbuf();
auto data_source = buffer.str();
std::string_view data_view(data_source.data(), data_source.size());
try {
auto obj = daw::json::from_json<std::vector<variant_type>>(data_view);
auto obj_json = daw::json::to_json(
obj, daw::json::options::output_flags<daw::json::options::SerializationFormat::Pretty>);
std::cout << obj_json.c_str() << std::endl;
} catch (const daw::json::json_exception &err) {
std::cout << daw::json::to_formatted_string(err, data_source.data()) << std::endl;
throw err;
}
}After compiling , if you run this with the following test.json content:
[
{
"dtype": "BAR",
"field_1": "val_0"
},
{
"field_1": 3,
"dtype": "FOO"
}
]It works as expected. However, if you use swap the order of dtype and field_1 in the first element, like so:
[
{
"field_1": "val_0",
"dtype": "BAR"
},
{
"field_1": 3,
"dtype": "FOO"
}
]then you get the following error:
reason: Invalid or corrupt string
location: near line: 3 col: 18
"[ { "field_1": "v"
terminate called after throwing an instance of 'daw::json::v3_24_0d::json_exception'
what(): Invalid or corrupt string
Aborted
Curiously, if you change bar_type::field_1 to a string type instead of an enum type, then it now parses correctly again.
This seems to imply that the discriminator tag cannot appear after a serialize-by-name enum field? Any idea what's the bug?
Reactions are currently unavailable