diff --git a/CMakeLists.txt b/CMakeLists.txt index 74f4013..e2fb0ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ if(valijson_BUILD_TESTS) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) + list(APPEND TEST_SOURCES tests/test_boost_json_adapter.cpp) list(APPEND TEST_SOURCES tests/test_property_tree_adapter.cpp) endif() @@ -153,7 +154,7 @@ if(valijson_BUILD_TESTS) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) - target_compile_definitions(test_suite PRIVATE "VALIJSON_BUILD_PROPERTY_TREE_ADAPTER") + target_compile_definitions(test_suite PRIVATE "VALIJSON_BUILD_BOOST_ADAPTERS") endif() if(Poco_FOUND) diff --git a/include/valijson/adapters/boost_json_adapter.hpp b/include/valijson/adapters/boost_json_adapter.hpp new file mode 100644 index 0000000..4c1ee55 --- /dev/null +++ b/include/valijson/adapters/boost_json_adapter.hpp @@ -0,0 +1,719 @@ +/** + * @file + * + * @brief Adapter implementation for the Boost.JSON library. + * + * Include this file in your program to enable support for boost Boost.JSONs. + * + * This file defines the following classes (not in this order): + * - BoostJsonAdapter + * - BoostJsonArray + * - BoostJsonArrayValueIterator + * - BoostJsonFrozenValue + * - BoostJsonObject + * - BoostJsonObjectMember + * - BoostJsonObjectMemberIterator + * - BoostJsonValue + * + * Due to the dependencies that exist between these classes, the ordering of + * class declarations and definitions may be a bit confusing. The best place to + * start is BoostJsonAdapter. This class definition is actually very small, + * since most of the functionality is inherited from the BasicAdapter class. + * Most of the classes in this file are provided as template arguments to the + * inherited BasicAdapter class. + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace valijson { +namespace adapters { + +class BoostJsonAdapter; +class BoostJsonArrayValueIterator; +class BoostJsonObjectMemberIterator; + +typedef std::pair BoostJsonObjectMember; + +/** + * @brief Light weight wrapper for a Boost.JSON that contains + * array-like data. + * + * This class is light weight wrapper for a Boost.JSON array. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * Boost.JSON value, assumed to be an array, so there is very little overhead + * associated with copy construction and passing by value. + */ +class BoostJsonArray +{ +public: + + typedef BoostJsonArrayValueIterator const_iterator; + typedef BoostJsonArrayValueIterator iterator; + + /// Construct a BoostJsonArray referencing an empty array. + BoostJsonArray() + : m_value(emptyArray()) { } + + /** + * @brief Construct BoostJsonArray referencing a specific Boost.JSON + * value. + * + * @param value reference to a Boost.JSON value + * + * Note that this constructor will throw an exception if the value is not + * an array. + */ + explicit BoostJsonArray(const boost::json::value &value) + : m_value(value.as_array()) { + // boost::json::value::as_array() will already have thrown an exception + // if the underlying value is not an array + } + + /** + * @brief Return an iterator for the first element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying Boost.JSON implementation. + */ + BoostJsonArrayValueIterator begin() const; + + /** + * @brief Return an iterator for one-past the last element of the array. + * + * The iterator return by this function is effectively the iterator + * returned by the underlying Boost.JSON implementation. + */ + BoostJsonArrayValueIterator end() const; + + /// Return the number of elements in the array + size_t size() const + { + return m_value.size(); + } + +private: + /** + * @brief Return a reference to a Boost.JSON value that is an empty array. + * + * Note that the value returned by this function is a singleton. + */ + static const boost::json::array & emptyArray() + { + static const boost::json::array array; + return array; + } + + /// Reference to the contained value + const boost::json::array &m_value; +}; + +/** + * @brief Light weight wrapper for a Boost.JSON object. + * + * This class is light weight wrapper for a Boost.JSON. It provides a + * minimum set of container functions and typedefs that allow it to be used as + * an iterable container. + * + * An instance of this class contains a single reference to the underlying + * Boost.JSON value, assumed to be an object, so there is very little overhead + * associated with copy construction and passing by value. + */ +class BoostJsonObject +{ +public: + + typedef BoostJsonObjectMemberIterator const_iterator; + typedef BoostJsonObjectMemberIterator iterator; + + /// Construct a BoostJsonObject referencing an empty object singleton. + BoostJsonObject() + : m_value(emptyObject()) { } + + /** + * @brief Construct a BoostJsonObject referencing a specific Boost.JSON + * value. + * + * @param value reference to a Boost.JSON value + * + * Note that this constructor will throw an exception if the value is not + * an object. + */ + BoostJsonObject(const boost::json::value &value) + : m_value(value.as_object()) + { + // boost::json::value::as_object() will already have thrown an exception + // if the underlying value is not an array + } + + /** + * @brief Return an iterator for this first object member + * + * The iterator return by this function is effectively a wrapper around + * the iterator value returned by the underlying Boost.JSON + * implementation. + */ + BoostJsonObjectMemberIterator begin() const; + + /** + * @brief Return an iterator for an invalid object member that indicates + * the end of the collection. + * + * The iterator return by this function is effectively a wrapper around + * the pointer value returned by the underlying Boost.JSON + * implementation. + */ + BoostJsonObjectMemberIterator end() const; + + /** + * @brief Return an iterator for the object member with the specified + * property name. + * + * If an object member with the specified name does not exist, the iterator + * returned will be the same as the iterator returned by the end() function. + * + * @param property property name to search for + */ + BoostJsonObjectMemberIterator find(const std::string &property) const; + + /// Returns the number of members belonging to this object. + size_t size() const + { + return m_value.size(); + } + +private: + + /** + * @brief Return a reference to an empty Boost.JSON. + * + * Note that the value returned by this function is a singleton. + */ + static const boost::json::object & emptyObject() + { + static const boost::json::object object; + return object; + } + + /// Reference to the contained object + const boost::json::object &m_value; + +}; + +/** + * @brief Stores an independent copy of a Boost.JSON value. + * + * This class allows a Boost.JSON value to be stored independent of its + * original 'document'. Boost.JSON makes this easy to do, as it does + * not require you to use custom memory management. + * + * @see FrozenValue + */ +class BoostJsonFrozenValue: public FrozenValue +{ +public: + + /** + * @brief Make a copy of a Boost.JSON value + * + * @param source the Boost.JSON value to be copied + */ + explicit BoostJsonFrozenValue(const boost::json::value &source) + : m_value(source) { } + + FrozenValue * clone() const override + { + return new BoostJsonFrozenValue(m_value); + } + + bool equalTo(const Adapter &other, bool strict) const override; + +private: + + /// Stored Boost.JSON value + boost::json::value m_value; +}; + +/** + * @brief Light weight wrapper for a Boost.JSON value. + * + * This class is passed as an argument to the BasicAdapter template class, + * and is used to provide access to a Boost.JSON value. This class is + * responsible for the mechanics of actually reading a Boost.JSON value, whereas + * BasicAdapter class is responsible for the semantics of type comparisons + * and conversions. + * + * The functions that need to be provided by this class are defined implicitly + * by the implementation of the BasicAdapter template class. + * + * @see BasicAdapter + */ +class BoostJsonValue +{ +public: + + /// Construct a wrapper for the empty object singleton + BoostJsonValue() + : m_value(emptyObject()) { } + + /** + * @brief Construct a BoostJsonValue for for a specific Boost.JSON value + */ + BoostJsonValue(const boost::json::value &value) + : m_value(value) { } + + /** + * @brief Create a new BoostJsonFrozenValue instance that contains the + * value referenced by this BoostJsonValue instance. + * + * @returns pointer to a new BoostJsonFrozenValue instance, belonging to + * the caller. + */ + FrozenValue* freeze() const + { + return new BoostJsonFrozenValue(m_value); + } + + /** + * @brief Return an instance of BoostJsonArrayAdapter. + * + * If the referenced Boost.JSON value is an array, this function will + * return a std::optional containing a BoostJsonArray instance + * referencing the array. + * + * Otherwise it will return an empty optional. + */ + opt::optional getArrayOptional() const + { + if (m_value.is_array()) { + return opt::make_optional(BoostJsonArray(m_value)); + } + + return {}; + } + + /** + * @brief Retrieve the number of elements in the array + * + * If the referenced Boost.JSON value is an array, this function will + * retrieve the number of elements in the array and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of elements was retrieved, false otherwise. + */ + bool getArraySize(size_t &result) const + { + if (m_value.is_array()) { + result = m_value.get_array().size(); + return true; + } + + return false; + } + + bool getBool(bool &result) const + { + if (m_value.is_bool()) { + result = m_value.get_bool(); + return true; + } + + return false; + } + + bool getDouble(double &result) const + { + if (m_value.is_double()) { + result = m_value.get_double(); + return true; + } + + return false; + } + + bool getInteger(int64_t &result) const + { + if(m_value.is_int64()) { + result = m_value.get_int64(); + return true; + } + return false; + } + + /** + * @brief Optionally return a BoostJsonObject instance. + * + * If the referenced Boost.JSON is an object, this function will return a + * std::optional containing a BoostJsonObject instance referencing the + * object. + * + * Otherwise it will return an empty optional. + */ + opt::optional getObjectOptional() const + { + if (m_value.is_object()) { + return opt::make_optional(BoostJsonObject(m_value)); + } + +#if __cplusplus >= 201703 + // std::nullopt is available since C++17 + return std::nullopt; +#else + // This is the older way to achieve the same result, but potentially at the cost of a compiler warning + return {}; +#endif + } + + /** + * @brief Retrieve the number of members in the object + * + * If the referenced Boost.JSON value is an object, this function will + * retrieve the number of members in the object and store it in the output + * variable provided. + * + * @param result reference to size_t to set with result + * + * @returns true if the number of members was retrieved, false otherwise. + */ + bool getObjectSize(size_t &result) const + { + if (m_value.is_object()) { + result = m_value.get_object().size(); + return true; + } + + return false; + } + + bool getString(std::string &result) const + { + if (m_value.is_string()) { + result = m_value.get_string().c_str(); + return true; + } + + return false; + } + + static bool hasStrictTypes() + { + return true; + } + + bool isArray() const + { + return m_value.is_array(); + } + + bool isBool() const + { + return m_value.is_bool(); + } + + bool isDouble() const + { + return m_value.is_double(); + } + + bool isInteger() const + { + return m_value.is_int64(); + } + + bool isNull() const + { + return m_value.is_null(); + } + + bool isNumber() const + { + return m_value.is_number(); + } + + bool isObject() const + { + return m_value.is_object(); + } + + bool isString() const + { + return m_value.is_string(); + } + +private: + + /// Return a reference to an empty object singleton + static const boost::json::value & emptyObject() + { + static const boost::json::value object; + return object; + } + + /// Reference to the contained Boost.JSON value. + const boost::json::value &m_value; +}; + +/** + * @brief An implementation of the Adapter interface supporting the Boost.JSON + * library. + * + * This class is defined in terms of the BasicAdapter template class, which + * helps to ensure that all of the Adapter implementations behave consistently. + * + * @see Adapter + * @see BasicAdapter + */ +class BoostJsonAdapter: + public BasicAdapter +{ +public: + + /// Construct a BoostJsonAdapter that contains an empty object + BoostJsonAdapter() + : BasicAdapter() { } + + /// Construct a BoostJsonAdapter using a specific Boost.JSON value + BoostJsonAdapter(const boost::json::value &value) + : BasicAdapter(value) { } +}; + +/** + * @brief Class for iterating over values held in a JSON array. + * + * This class provides a JSON array iterator that dereferences as an instance of + * BoostJsonAdapter representing a value stored in the array. + * + * @see BoostJsonArray + */ +class BoostJsonArrayValueIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = BoostJsonAdapter; + using difference_type = BoostJsonAdapter; + using pointer = BoostJsonAdapter*; + using reference = BoostJsonAdapter&; + + /** + * @brief Construct a new BoostJsonArrayValueIterator using an existing + * Boost.JSON iterator. + * + * @param itr Boost.JSON iterator to store + */ + BoostJsonArrayValueIterator( + const boost::json::array::const_iterator itr) + : m_itr(itr) { } + + /// Returns a BoostJsonAdapter that contains the value of the current + /// element. + BoostJsonAdapter operator*() const + { + return BoostJsonAdapter(*m_itr); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator against another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other iterator to compare against + * + * @returns true if the iterators are equal, false otherwise. + */ + bool operator==(const BoostJsonArrayValueIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const BoostJsonArrayValueIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const BoostJsonArrayValueIterator& operator++() + { + m_itr++; + + return *this; + } + + BoostJsonArrayValueIterator operator++(int) + { + BoostJsonArrayValueIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const BoostJsonArrayValueIterator& operator--() + { + m_itr--; + + return *this; + } + + void advance(std::ptrdiff_t n) + { + m_itr += n; + } + +private: + + boost::json::array::const_iterator m_itr; +}; + +/** + * @brief Class for iterating over the members belonging to a JSON object. + * + * This class provides a JSON object iterator that dereferences as an instance + * of BoostJsonObjectMember representing one of the members of the object. + * + * @see BoostJsonObject + * @see BoostJsonObjectMember + */ +class BoostJsonObjectMemberIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = BoostJsonObjectMember; + using difference_type = BoostJsonObjectMember; + using pointer = BoostJsonObjectMember*; + using reference = BoostJsonObjectMember&; + + /** + * @brief Construct an iterator from a BoostJson iterator. + * + * @param itr BoostJson iterator to store + */ + BoostJsonObjectMemberIterator(boost::json::object::const_iterator itr) + : m_itr(itr) { } + + /** + * @brief Returns a BoostJsonObjectMember that contains the key and + * value belonging to the object member identified by the iterator. + */ + BoostJsonObjectMember operator*() const + { + return BoostJsonObjectMember(m_itr->key(), m_itr->value()); + } + + DerefProxy operator->() const + { + return DerefProxy(**this); + } + + /** + * @brief Compare this iterator with another iterator. + * + * Note that this directly compares the iterators, not the underlying + * values, and assumes that two identical iterators will point to the same + * underlying object. + * + * @param other Iterator to compare with + * + * @returns true if the underlying iterators are equal, false otherwise + */ + bool operator==(const BoostJsonObjectMemberIterator &other) const + { + return m_itr == other.m_itr; + } + + bool operator!=(const BoostJsonObjectMemberIterator &other) const + { + return !(m_itr == other.m_itr); + } + + const BoostJsonObjectMemberIterator& operator++() + { + m_itr++; + + return *this; + } + + BoostJsonObjectMemberIterator operator++(int) + { + BoostJsonObjectMemberIterator iterator_pre(m_itr); + ++(*this); + return iterator_pre; + } + + const BoostJsonObjectMemberIterator& operator--() + { + m_itr--; + + return *this; + } + +private: + + /// Iternal copy of the original Boost.JSON iterator + boost::json::object::const_iterator m_itr; +}; + +/// Specialisation of the AdapterTraits template struct for BoostJsonAdapter. +template<> +struct AdapterTraits +{ + typedef boost::json::value DocumentType; + + static std::string adapterName() + { + return "BoostJsonAdapter"; + } +}; + +inline bool BoostJsonFrozenValue::equalTo(const Adapter &other, bool strict) const +{ + return BoostJsonAdapter(m_value).equalTo(other, strict); +} + +inline BoostJsonArrayValueIterator BoostJsonArray::begin() const +{ + return m_value.cbegin(); +} + +inline BoostJsonArrayValueIterator BoostJsonArray::end() const +{ + return m_value.cend(); +} + +inline BoostJsonObjectMemberIterator BoostJsonObject::begin() const +{ + return m_value.cbegin(); +} + +inline BoostJsonObjectMemberIterator BoostJsonObject::end() const +{ + return m_value.cend(); +} + +inline BoostJsonObjectMemberIterator BoostJsonObject::find( + const std::string &propertyName) const +{ + return m_value.find(propertyName); +} + +} // namespace adapters +} // namespace valijson diff --git a/include/valijson/utils/boost_json_utils.hpp b/include/valijson/utils/boost_json_utils.hpp new file mode 100644 index 0000000..1168903 --- /dev/null +++ b/include/valijson/utils/boost_json_utils.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include +#include + +namespace valijson { +namespace utils { + +inline bool loadDocument(const std::string &path, boost::json::value &document) +{ + // Load schema JSON from file + std::string file; + if (!loadFile(path, file)) { + std::cerr << "Failed to load json from file '" << path << "'." + << std::endl; + return false; + } + + // Parse schema +#if VALIJSON_USE_EXCEPTION + try { +#endif + boost::json::error_code errorCode; + boost::json::string_view stringView{file}; + document = boost::json::parse(stringView, errorCode); + if (errorCode) { + std::cerr << "Boost.JSON parsing error: " << errorCode.message(); + return false; + } +#if VALIJSON_USE_EXCEPTION + } catch (std::exception const & exception) { + std::cerr << "Boost.JSON parsing exception: " << exception.what(); + return false; + } +#endif + + return true; +} + +} // namespace utils +} // namespace valijson diff --git a/tests/test_adapter_comparison.cpp b/tests/test_adapter_comparison.cpp index b269626..b531efd 100644 --- a/tests/test_adapter_comparison.cpp +++ b/tests/test_adapter_comparison.cpp @@ -20,9 +20,11 @@ #include #include -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS #include #include +#include +#include #endif #ifdef VALIJSON_BUILD_QT_ADAPTER @@ -157,7 +159,7 @@ TEST_F(TestAdapterComparison, JsonCppVsPicoJson) valijson::adapters::PicoJsonAdapter>(); } -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, JsonCppVsPropertyTree) { @@ -166,6 +168,13 @@ TEST_F(TestAdapterComparison, JsonCppVsPropertyTree) valijson::adapters::PropertyTreeAdapter>(); } +TEST_F(TestAdapterComparison, JsonCppVsBoostJson) +{ + testComparison< + valijson::adapters::JsonCppAdapter, + valijson::adapters::BoostJsonAdapter>(); +} + #endif TEST_F(TestAdapterComparison, JsonCppVsRapidJson) @@ -184,12 +193,12 @@ TEST_F(TestAdapterComparison, JsonCppVsRapidJsonCrtAlloc) rapidjson::CrtAllocator> > >(); } + +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS // // PropertyTreeAdapter vs X // ------------------------------------------------------------------------------------------------ -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER - TEST_F(TestAdapterComparison, PropertyTreeVsPicoJson) { testComparison< @@ -220,6 +229,40 @@ TEST_F(TestAdapterComparison, PropertyTreeVsRapidJsonCrtAlloc) rapidjson::CrtAllocator> > >(); } +// +// BoostJsonAdapter vs X +// ------------------------------------------------------------------------------------------------ + +TEST_F(TestAdapterComparison, BoostJsonVsPicoJson) +{ + testComparison< + valijson::adapters::BoostJsonAdapter, + valijson::adapters::PicoJsonAdapter>(); +} + +TEST_F(TestAdapterComparison, BoostJsonVsBoostJson) +{ + testComparison< + valijson::adapters::BoostJsonAdapter, + valijson::adapters::BoostJsonAdapter>(); +} + +TEST_F(TestAdapterComparison, BoostJsonVsRapidJson) +{ + testComparison< + valijson::adapters::BoostJsonAdapter, + valijson::adapters::RapidJsonAdapter>(); +} + +TEST_F(TestAdapterComparison, BoostJsonVsRapidJsonCrtAlloc) +{ + testComparison< + valijson::adapters::BoostJsonAdapter, + valijson::adapters::GenericRapidJsonAdapter< + rapidjson::GenericValue, + rapidjson::CrtAllocator> > >(); +} + #endif // @@ -322,7 +365,7 @@ TEST_F(TestAdapterComparison, Json11VsPicoJson) valijson::adapters::PicoJsonAdapter>(); } -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, Json11VsPropertyTree) { @@ -331,7 +374,14 @@ TEST_F(TestAdapterComparison, Json11VsPropertyTree) valijson::adapters::PropertyTreeAdapter>(); } -#endif // VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +TEST_F(TestAdapterComparison, Json11VsBoostJson) +{ + testComparison< + valijson::adapters::Json11Adapter, + valijson::adapters::BoostJsonAdapter>(); +} + +#endif // VALIJSON_BUILD_BOOST_ADAPTERS // // NlohmannJsonAdapter vs X @@ -381,7 +431,7 @@ TEST_F(TestAdapterComparison, NlohmannJsonVsPicoJson) valijson::adapters::PicoJsonAdapter>(); } -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, NlohmannJsonVsPropertyTree) { @@ -390,7 +440,14 @@ TEST_F(TestAdapterComparison, NlohmannJsonVsPropertyTree) valijson::adapters::PropertyTreeAdapter>(); } -#endif // VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +TEST_F(TestAdapterComparison, NlohmannJsonVsBoostJson) +{ + testComparison< + valijson::adapters::NlohmannJsonAdapter, + valijson::adapters::BoostJsonAdapter>(); +} + +#endif // VALIJSON_BUILD_BOOST_ADAPTERS // // QtJsonAdapter vs X @@ -434,7 +491,7 @@ TEST_F(TestAdapterComparison, QtJsonVsPicoJson) valijson::adapters::PicoJsonAdapter>(); } -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, QtJsonVsPropertyTree) { @@ -443,7 +500,14 @@ TEST_F(TestAdapterComparison, QtJsonVsPropertyTree) valijson::adapters::PropertyTreeAdapter>(); } -#endif // VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +TEST_F(TestAdapterComparison, QtJsonVsBoostJson) +{ + testComparison< + valijson::adapters::QtJsonAdapter, + valijson::adapters::BoostJsonAdapter>(); +} + +#endif // VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, QtJsonVsJson11) { @@ -504,7 +568,7 @@ TEST_F(TestAdapterComparison, PocoJsonVsPicoJson) valijson::adapters::PicoJsonAdapter>(); } -#ifdef VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +#ifdef VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, PocoJsonVsPropertyTree) { @@ -513,7 +577,14 @@ TEST_F(TestAdapterComparison, PocoJsonVsPropertyTree) valijson::adapters::PropertyTreeAdapter>(); } -#endif // VALIJSON_BUILD_PROPERTY_TREE_ADAPTER +TEST_F(TestAdapterComparison, PocoJsonVsBoostJson) +{ + testComparison< + valijson::adapters::PocoJsonAdapter, + valijson::adapters::BoostJsonAdapter>(); +} + +#endif // VALIJSON_BUILD_BOOST_ADAPTERS TEST_F(TestAdapterComparison, PocoJsonVsJson11) { diff --git a/tests/test_boost_json_adapter.cpp b/tests/test_boost_json_adapter.cpp new file mode 100644 index 0000000..bd88060 --- /dev/null +++ b/tests/test_boost_json_adapter.cpp @@ -0,0 +1,89 @@ +#include + +#include // Needs to be included exactly once in the code to use header-only version of Boost.JSON + +#include + +class TestBoostJsonAdapter : public testing::Test +{ + +}; + +TEST_F(TestBoostJsonAdapter, BasicArrayIteration) +{ + const unsigned int numElements = 10; + + // Create a Json document that consists of an array of numbers + boost::json::array array; + for (unsigned int i = 0; i < numElements; i++) { + // Boost.JSON differs from some other libraries in offering emplace_back() + // as well as push_back(). Using the former here saves us having to create + // a temporary. + array.emplace_back(static_cast(i)); + } + boost::json::value document(array); + + // Ensure that wrapping the document preserves the array and does not allow + // it to be cast to other types + valijson::adapters::BoostJsonAdapter adapter(document); +#if VALIJSON_USE_EXCEPTIONS + ASSERT_NO_THROW( adapter.getArray() ); + ASSERT_ANY_THROW( adapter.getBool() ); + ASSERT_ANY_THROW( adapter.getDouble() ); + ASSERT_ANY_THROW( adapter.getObject() ); + ASSERT_ANY_THROW( adapter.getString() ); +#endif + + // Ensure that the array contains the expected number of elements + EXPECT_EQ( numElements, adapter.getArray().size() ); + + // Ensure that the elements are returned in the order they were inserted + unsigned int expectedValue = 0; + for (const valijson::adapters::BoostJsonAdapter value : adapter.getArray()) { + ASSERT_TRUE( value.isNumber() ); + EXPECT_EQ( double(expectedValue), value.getDouble() ); + expectedValue++; + } + + // Ensure that the correct number of elements were iterated over + EXPECT_EQ(numElements, expectedValue); +} + +TEST_F(TestBoostJsonAdapter, BasicObjectIteration) +{ + const unsigned int numElements = 10; + + // Create a DropBoxJson document that consists of an object that maps numeric + // strings their corresponding numeric values + boost::json::object object; + for (uint32_t i = 0; i < numElements; i++) { + object[std::to_string(i)] = static_cast(i); + } + boost::json::value document(object); + + // Ensure that wrapping the document preserves the object and does not + // allow it to be cast to other types + valijson::adapters::BoostJsonAdapter adapter(document); +#if VALIJSON_USE_EXCEPTIONS + ASSERT_NO_THROW( adapter.getObject() ); + ASSERT_ANY_THROW( adapter.getArray() ); + ASSERT_ANY_THROW( adapter.getBool() ); + ASSERT_ANY_THROW( adapter.getDouble() ); + ASSERT_ANY_THROW( adapter.getString() ); +#endif + + // Ensure that the object contains the expected number of members + EXPECT_EQ( numElements, adapter.getObject().size() ); + + // Ensure that the members are returned in the order they were inserted + unsigned int expectedValue = 0; + for (const valijson::adapters::BoostJsonAdapter::ObjectMember member : adapter.getObject()) { + ASSERT_TRUE( member.second.isNumber() ); + EXPECT_EQ( std::to_string(expectedValue), member.first ); + EXPECT_EQ( double(expectedValue), member.second.getDouble() ); + expectedValue++; + } + + // Ensure that the correct number of elements were iterated over + EXPECT_EQ( numElements, expectedValue ); +}