diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index e3be5eb1..078402ae 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -2,9 +2,9 @@ name: Clang Format Diff on: push: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] pull_request: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] jobs: build: diff --git a/.github/workflows/linux-clang.yml b/.github/workflows/linux-clang.yml index e2e7f01c..401c8570 100644 --- a/.github/workflows/linux-clang.yml +++ b/.github/workflows/linux-clang.yml @@ -2,9 +2,9 @@ name: Ubuntu (clang) on: push: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] pull_request: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] jobs: build: diff --git a/.github/workflows/linux-gcc.yml b/.github/workflows/linux-gcc.yml index caaba1ef..1e0596f8 100644 --- a/.github/workflows/linux-gcc.yml +++ b/.github/workflows/linux-gcc.yml @@ -2,9 +2,9 @@ name: Ubuntu (gcc) on: push: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] pull_request: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] jobs: build: diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 035f72de..c334ff00 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -2,9 +2,9 @@ name: macOS Monterey 12 on: push: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] pull_request: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] jobs: build: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index a93d8f62..6a9583d8 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,9 +2,9 @@ name: Windows Server 2022 on: push: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] pull_request: - branches: [ master, fullsupport_xml] + branches: [ master, improve_xml] jobs: build: diff --git a/CMakeLists.txt b/CMakeLists.txt index 3822a92c..b463ae1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ set(TEST_YAML test/test_yaml.cpp test/test_yaml_bech.cpp) set(YAMLBENCH benchmark/yaml_benchmark.cpp) set(TEST_NOTHROW test/test_yaml_nothrow.cpp) set(TEST_UTIL test/test_util.cpp) +set(TEST_XMLNOTHROW test/test_xml_nothrow.cpp) add_executable(json_example ${JSON_EXAMPLE}) add_executable(xml_example ${XML_EXAMPLE}) @@ -67,6 +68,7 @@ add_executable(test_yaml ${TEST_YAML}) add_executable(yaml_benchmark ${YAMLBENCH}) add_executable(test_nothrow ${TEST_NOTHROW}) add_executable(test_util ${TEST_UTIL}) +add_executable(test_xml_nothrow ${TEST_XMLNOTHROW}) # unit test option(BUILD_UNIT_TESTS "Build unit tests" ON) @@ -91,3 +93,4 @@ add_test(NAME test_xml COMMAND test_xml) add_test(NAME test_yaml COMMAND test_yaml) add_test(NAME test_nothrow COMMAND test_nothrow) add_test(NAME test_util COMMAND test_util) +add_test(NAME test_xml_nothrow COMMAND test_xml_nothrow) diff --git a/README.md b/README.md index e80960b1..7b3cd433 100644 --- a/README.md +++ b/README.md @@ -96,14 +96,10 @@ person p = {"admin", 20}; iguana::string_stream ss; // here use std::string is also ok iguana::to_xml(ss, p); std::cout << ss.str() << std::endl; -// you can also call iguana::to_xml_pretty to get pretty string -iguana::string_stream s; -iguana::to_xml_pretty(s, p); -std::cout << s.str() << std::endl; // deserialization the structure from the string std::string xml = " buke 30"; -iguana::from_xml(p, xml.data()); +iguana::from_xml(p, xml); ``` #### Serialization of yaml @@ -212,7 +208,7 @@ std::string str = R"( )"; library_t lib; -iguana::from_xml(lib, str.data()); +iguana::from_xml(lib, str); ``` #### yaml diff --git a/iguana/json_writer.hpp b/iguana/json_writer.hpp index 7dd5c770..4134f4f8 100644 --- a/iguana/json_writer.hpp +++ b/iguana/json_writer.hpp @@ -174,12 +174,12 @@ template IGUANA_INLINE void render_json_value(Stream &s, T &&t) { using U = typename std::decay_t; s.push_back('['); - const size_t size = std::tuple_size_v; + constexpr size_t size = std::tuple_size_v; for_each(std::forward(t), [&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA { render_json_value(s, v); - if (i != size - 1) + if (i != size - 1) [[likely]] s.push_back(','); }); s.push_back(']'); diff --git a/iguana/xml_reader.hpp b/iguana/xml_reader.hpp index 53488d52..0f8b1384 100644 --- a/iguana/xml_reader.hpp +++ b/iguana/xml_reader.hpp @@ -159,6 +159,30 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, parse_item(value.value(), it, end, name); } +// /> or skip and until the +// loose inspection here +template +IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) { + skip_till_greater(it, end); + ++it; + if (*(it - 2) == '/') { + // .../> + return; + } + // + size_t size = name.size(); + while (it != end) { + skip_till_greater(it, end); + if (*(it - size - 2) == '<' && *(it - size - 1) == '/' && + (std::string_view{&*(it - size), size} == name)) { + ++it; + return; + } + ++it; + } + throw std::runtime_error("unclosed tag: " + std::string(name)); +} + template IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, std::string_view name) { @@ -241,12 +265,16 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, "type must be memberptr"); using V = std::remove_reference_t; if constexpr (!cdata_t) { - detail::parse_item(value.*member_ptr, it, end, key); + parse_item(value.*member_ptr, it, end, key); } }, member_it->second); } else [[unlikely]] { +#ifdef THROW_UNKNOWN_KEY throw std::runtime_error("Unknown key: " + std::string(key)); +#else + skip_object_value(it, end, key); +#endif } skip_sapces_and_newline(it, end); } diff --git a/test/test_xml_nothrow.cpp b/test/test_xml_nothrow.cpp new file mode 100644 index 00000000..c5941136 --- /dev/null +++ b/test/test_xml_nothrow.cpp @@ -0,0 +1,77 @@ +#define DOCTEST_CONFIG_IMPLEMENT +#include "doctest.h" +#undef THROW_UNKNOWN_KEY +#include "iguana/xml_reader.hpp" +#include "iguana/xml_writer.hpp" +#include +#include +#include +#include +#include +#include + +enum class enum_status { + paid, + unpaid, +}; +struct order_t { + enum_status status; + float total; +}; +REFLECTION(order_t, status, total); + +TEST_CASE("test unkonwn key") { + auto validator = [](order_t od) { + CHECK(od.status == enum_status::unpaid); + CHECK(od.total == 65.0f); + }; + std::string str = R"( + + 12345 + 1 + + + John + ]]> + Doe +
+ 123 Main St + Anytown + CA + 12345 +
+
+ + + 67890 + Widget A + 2 + 10.00 + + + 98765 + Widget B + 3 + 15.00 + + + ]]> + 65.00 +
+ )"; + order_t od; + iguana::from_xml(od, str); + validator(od); + + std::string ss; + iguana::to_xml(od, ss); + order_t od1; + iguana::from_xml(od1, ss); + validator(od1); +} + +// doctest comments +// 'function' : must be 'attribute' - see issue #182 +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) +int main(int argc, char **argv) { return doctest::Context(argc, argv).run(); } +DOCTEST_MSVC_SUPPRESS_WARNING_POP