Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/generic.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ reflect-cpp is intended to be used to directly parse into structs. Doing this is
But in some cases, we simply cannot anticipate the complete structure of the data at compile time. For these cases, we
have `rfl::Generic`.

Note that generics are not supported for Avro.
Note that generics are not supported for Avro. For XML, generics are supported but only for writing, not for reading.

`rfl::Generic` is a convenience wrapper around the following type:

Expand Down
48 changes: 37 additions & 11 deletions include/rfl/xml/Parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,65 @@

#include <type_traits>

#include "../Generic.hpp"
#include "../always_false.hpp"
#include "../internal/is_attribute.hpp"
#include "../parsing/NamedTupleParser.hpp"
#include "../parsing/Parser.hpp"
#include "Reader.hpp"
#include "Writer.hpp"

namespace rfl {
namespace parsing {
namespace rfl::parsing {

/// XML is very special. It doesn't have proper support for arrays, which means
/// that we just need to ignore empty containers. Therefore, we need to a
/// template specialization for the NamedTuple parser to accommodate for it.
template <class ProcessorsType, class... FieldTypes>
requires AreReaderAndWriter<xml::Reader, xml::Writer, NamedTuple<FieldTypes...>>
requires AreReaderAndWriter<xml::Reader, xml::Writer,
NamedTuple<FieldTypes...>>
struct Parser<xml::Reader, xml::Writer, NamedTuple<FieldTypes...>,
ProcessorsType>
: public NamedTupleParser<xml::Reader, xml::Writer,
/*_ignore_empty_containers=*/true,
/*_all_required=*/ProcessorsType::all_required_,
/*_no_field_names_=*/false, ProcessorsType,
FieldTypes...> {
};
FieldTypes...> {};

/// The generic parser is also special, because we need to ignore empty
/// containers, which means that we need to try to read the value as an array or
/// object before trying to read it as a string. Therefore, we need to a
/// template specialization for the generic parser to accommodate for it.
template <class ProcessorsType>
requires AreReaderAndWriter<xml::Reader, xml::Writer, Generic>
struct Parser<xml::Reader, xml::Writer, Generic, ProcessorsType> {
using InputVarType = typename xml::Reader::InputVarType;

static Result<Generic> read(const xml::Reader& _r,
const InputVarType& _var) noexcept {
static_assert(always_false_v<ProcessorsType>,
"XML does not support reading generic types.");
return error("XML does not support reading generic types.");
}

} // namespace parsing
} // namespace rfl
static void write(const xml::Writer& _w, const Generic& _generic,
const auto& _parent) {
Parser<xml::Reader, xml::Writer, Generic::ReflectionType,
ProcessorsType>::write(_w, _generic.reflection(), _parent);
}

static schema::Type to_schema(
std::map<std::string, schema::Type>* _definitions) {
return Parser<xml::Reader, xml::Writer, Generic::ReflectionType,
ProcessorsType>::to_schema(_definitions);
}
};

namespace rfl {
namespace xml {
} // namespace rfl::parsing

namespace rfl::xml {
template <class T, class ProcessorsType>
using Parser = parsing::Parser<Reader, Writer, T, ProcessorsType>;

}
} // namespace rfl
} // namespace rfl::xml

#endif
15 changes: 10 additions & 5 deletions include/rfl/xml/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
#include "../always_false.hpp"
#include "../parsing/is_view_reader.hpp"

namespace rfl {
namespace xml {
namespace rfl::xml {

struct Reader {
struct XMLInputArray {
Expand Down Expand Up @@ -85,7 +84,14 @@ struct Reader {
return std::visit(get_value, _var.node_or_attribute_);

} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
return std::visit(get_value, _var.node_or_attribute_) == "true";
const auto val = std::visit(get_value, _var.node_or_attribute_);
if (val == "true" || val == "1") {
return true;
} else if (val == "false" || val == "0") {
return false;
} else {
return error("Could not cast '" + val + "' to boolean.");
}

} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
const auto str = std::visit(get_value, _var.node_or_attribute_);
Expand Down Expand Up @@ -178,7 +184,6 @@ struct Reader {
}
};

} // namespace xml
} // namespace rfl
} // namespace rfl::xml

#endif
26 changes: 26 additions & 0 deletions tests/xml/test_generic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <cassert>
#include <rfl.hpp>
#include <string>

#include "write_and_read.hpp"

namespace test_generic {

TEST(xml, test_generic) {
rfl::Generic::Object cat;
cat["name"] = "Cachou";
cat["colour"] = "black";
cat["species"] = "cat";

const auto xml_string = rfl::xml::write<"cat">(cat);

EXPECT_EQ(xml_string,
R"(<?xml version="1.0" encoding="UTF-8"?>
<cat>
<name>Cachou</name>
<colour>black</colour>
<species>cat</species>
</cat>
)");
}
} // namespace test_generic
Loading