mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 01b4710ff8 | |||
| 049f70aef3 | |||
| d874ab7239 | |||
| a27a17484d | |||
| 20cea8b477 | |||
| 0517fb3aaa |
@@ -115,7 +115,6 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||
obj.emplace(std::move(val));
|
||||
obj = std::move(val);
|
||||
{
|
||||
obj.value_or(val)
|
||||
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <experimental/meta>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -94,8 +95,13 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input);
|
||||
template <class T>
|
||||
requires(std::is_class_v<T> && !container_but_not_string<T> &&
|
||||
!concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> &&
|
||||
!concepts::smart_pointer<T> &&
|
||||
!concepts::appendable_containers<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view>)
|
||||
!std::is_same_v<T, std::string_view> &&
|
||||
!std::is_same_v<T, const char*> &&
|
||||
!std::is_same_v<T, char>)
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
@@ -111,8 +117,127 @@ constexpr void atom(string_builder &b, const T &t) {
|
||||
b.append('}');
|
||||
}
|
||||
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
constexpr void atom(string_builder &b, const T &opt) {
|
||||
if (opt) {
|
||||
atom(b, opt.value());
|
||||
} else {
|
||||
b.append_raw("null");
|
||||
}
|
||||
}
|
||||
|
||||
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
|
||||
template <concepts::smart_pointer T>
|
||||
constexpr void atom(string_builder &b, const T &ptr) {
|
||||
if (ptr) {
|
||||
atom(b, *ptr);
|
||||
} else {
|
||||
b.append_raw("null");
|
||||
}
|
||||
}
|
||||
|
||||
// Support for enums - serialize as string representation using expand approach from P2996R12
|
||||
template <typename T>
|
||||
requires(std::is_enum_v<T>)
|
||||
void atom(string_builder &b, const T &e) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
std::string_view result = "<unnamed>";
|
||||
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
|
||||
if (e == [:enum_val:]) {
|
||||
result = std::meta::identifier_of(enum_val);
|
||||
}
|
||||
};
|
||||
|
||||
if (result != "<unnamed>") {
|
||||
b.append_raw("\"");
|
||||
b.append_raw(result);
|
||||
b.append_raw("\"");
|
||||
} else {
|
||||
// Fallback to integer if enum value not found
|
||||
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||
}
|
||||
#else
|
||||
// Fallback: serialize as integer if reflection not available
|
||||
atom(b, static_cast<std::underlying_type_t<T>>(e));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Support for appendable containers that don't have operator[] (sets, etc.)
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
constexpr void atom(string_builder &b, const T &container) {
|
||||
if (container.empty()) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
}
|
||||
b.append('[');
|
||||
bool first = true;
|
||||
for (const auto& item : container) {
|
||||
if (!first) {
|
||||
b.append(',');
|
||||
}
|
||||
first = false;
|
||||
atom(b, item);
|
||||
}
|
||||
b.append(']');
|
||||
}
|
||||
|
||||
// append functions that delegate to atom functions for primitive types
|
||||
template <class T>
|
||||
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires(std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view> ||
|
||||
std::is_same_v<T, const char *> ||
|
||||
std::is_same_v<T, char>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::optional_type T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::smart_pointer T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::string_view_keyed_map T>
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
// works for struct
|
||||
template <class Z> void append(string_builder &b, const Z &z) {
|
||||
template <class Z>
|
||||
requires(std::is_class_v<Z> && !container_but_not_string<Z> &&
|
||||
!concepts::string_view_keyed_map<Z> &&
|
||||
!concepts::optional_type<Z> &&
|
||||
!concepts::smart_pointer<Z> &&
|
||||
!concepts::appendable_containers<Z> &&
|
||||
!std::is_same_v<Z, std::string> &&
|
||||
!std::is_same_v<Z, std::string_view> &&
|
||||
!std::is_same_v<Z, const char*> &&
|
||||
!std::is_same_v<Z, char>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
[:expand(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include "simdjson/generic/ondemand/object.h"
|
||||
#include "simdjson/generic/ondemand/serialization.h"
|
||||
#include "simdjson/generic/ondemand/value.h"
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#include "simdjson/generic/ondemand/json_builder.h"
|
||||
#endif
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
@@ -35,6 +35,20 @@ inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> x);
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> x);
|
||||
inline simdjson_result<std::string_view> to_json_string(simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> x);
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
/**
|
||||
* Create a JSON string from any user-defined type using static reflection.
|
||||
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
|
||||
*/
|
||||
template<typename T>
|
||||
requires(!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::document> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::value> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::object> &&
|
||||
!std::same_as<T, SIMDJSON_IMPLEMENTATION::ondemand::array>)
|
||||
inline std::string to_json_string(const T& obj);
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
/**
|
||||
|
||||
@@ -255,10 +255,16 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser
|
||||
|
||||
static_assert(
|
||||
deserializable<value_type, ValT>,
|
||||
"The specified type inside the unique_ptr must itself be deserializable");
|
||||
"The specified type inside the optional must itself be deserializable");
|
||||
static_assert(
|
||||
std::is_default_constructible_v<value_type>,
|
||||
"The specified type inside the unique_ptr must default constructible.");
|
||||
"The specified type inside the optional must default constructible.");
|
||||
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullopt
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out.emplace();
|
||||
@@ -297,7 +303,7 @@ template<typename R>
|
||||
consteval auto expand(R range) {
|
||||
std::vector<std::meta::info> args;
|
||||
for (auto r : range) {
|
||||
args.push_back(reflect_value(r));
|
||||
args.push_back(reflect_constant(r));
|
||||
}
|
||||
return substitute(^^__impl::replicator, args);
|
||||
}
|
||||
@@ -328,6 +334,33 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
};
|
||||
return e;
|
||||
}
|
||||
|
||||
// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12
|
||||
template <typename T, typename ValT>
|
||||
requires(std::is_enum_v<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
|
||||
bool found = false;
|
||||
[:expand(std::meta::enumerators_of(^^T)):] >> [&]<auto enum_val>{
|
||||
if (!found && str == std::meta::identifier_of(enum_val)) {
|
||||
out = [:enum_val:];
|
||||
found = true;
|
||||
}
|
||||
};
|
||||
|
||||
return found ? SUCCESS : INCORRECT_TYPE;
|
||||
#else
|
||||
// Fallback: deserialize as integer if reflection not available
|
||||
std::underlying_type_t<T> int_val;
|
||||
SIMDJSON_TRY(val.get(int_val));
|
||||
out = static_cast<T>(int_val);
|
||||
return SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename simdjson_value, typename T>
|
||||
requires(user_defined_type<std::remove_cvref_t<T>>)
|
||||
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept {
|
||||
@@ -366,6 +399,10 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &
|
||||
// Unique pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<bool>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -375,6 +412,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<int64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -384,6 +425,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<uint64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -393,6 +438,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<double>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -402,6 +451,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_unique<std::string_view>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -415,6 +468,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_vi
|
||||
// Shared pointers
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<bool>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -424,6 +481,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) no
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<int64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -433,6 +494,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<uint64_t>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -442,6 +507,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<double>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -451,6 +520,10 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out)
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
|
||||
if (val.is_null()) {
|
||||
out.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!out) {
|
||||
out = std::make_shared<std::string_view>();
|
||||
if (!out) { return MEMALLOC; }
|
||||
@@ -460,6 +533,92 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_vi
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
// Explicit optional specializations
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, concepts::optional_type auto&out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullopt
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out.emplace();
|
||||
}
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
out.value() = std::string{str};
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, concepts::optional_type auto&out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullopt
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out.emplace();
|
||||
}
|
||||
int64_t temp;
|
||||
SIMDJSON_TRY(val.get_int64().get(temp));
|
||||
out.value() = static_cast<int>(temp);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Explicit smart pointer specializations for string and int types
|
||||
////////////////////////////////////////
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out = std::make_unique<std::string>();
|
||||
}
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
*out = std::string{str};
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out = std::make_shared<std::string>();
|
||||
}
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
*out = std::string{str};
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
|
||||
// Check if the value is null
|
||||
if (val.is_null()) {
|
||||
out.reset(); // Set to nullptr
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out = std::make_unique<int>();
|
||||
}
|
||||
int64_t temp;
|
||||
SIMDJSON_TRY(val.get_int64().get(temp));
|
||||
*out = static_cast<int>(temp);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||
|
||||
@@ -12,6 +12,28 @@ namespace simdjson {
|
||||
* @copydoc simdjson::builtin::builder
|
||||
*/
|
||||
namespace builder = builtin::builder;
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
/**
|
||||
* Create a JSON string from any user-defined type using static reflection.
|
||||
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
|
||||
*/
|
||||
template<typename T>
|
||||
requires(!std::same_as<T, builtin::ondemand::document> &&
|
||||
!std::same_as<T, builtin::ondemand::value> &&
|
||||
!std::same_as<T, builtin::ondemand::object> &&
|
||||
!std::same_as<T, builtin::ondemand::array>)
|
||||
inline std::string to_json_string(const T& obj) {
|
||||
builder::string_builder str_builder;
|
||||
append(str_builder, obj);
|
||||
std::string_view view;
|
||||
if (str_builder.view().get(view) == SUCCESS) {
|
||||
return std::string(view);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_ONDEMAND_H
|
||||
|
||||
@@ -3,6 +3,9 @@ include_directories(..)
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_cpp_test(static_reflection_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_comprehensive_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_enum_tests LABELS ondemand acceptance per_implementation)
|
||||
endif(SIMDJSON_STATIC_REFLECTION)
|
||||
# Copy the simdjson dll into the tests directory
|
||||
if(MSVC AND BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
bool test_primitive_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct PrimitiveTypes {
|
||||
bool bool_val;
|
||||
char char_val;
|
||||
int int_val;
|
||||
double double_val;
|
||||
float float_val;
|
||||
};
|
||||
|
||||
PrimitiveTypes test{true, 'X', 42, 3.14159, 2.71f};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"bool_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"char_val\":\"X\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"int_val\":42") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"double_val\":3.14159") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<PrimitiveTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
PrimitiveTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.bool_val, test.bool_val);
|
||||
ASSERT_EQUAL(deserialized.char_val, test.char_val);
|
||||
ASSERT_EQUAL(deserialized.int_val, test.int_val);
|
||||
ASSERT_EQUAL(deserialized.double_val, test.double_val);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_string_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct StringTypes {
|
||||
std::string string_val;
|
||||
std::string_view string_view_val;
|
||||
};
|
||||
|
||||
StringTypes test{"hello world", "test_view"};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"string_val\":\"hello world\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_view_val\":\"test_view\"") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<StringTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
StringTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.string_val, test.string_val);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_optional_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct OptionalTypes {
|
||||
std::optional<int> opt_int_with_value;
|
||||
std::optional<std::string> opt_string_with_value;
|
||||
std::optional<int> opt_int_null;
|
||||
std::optional<std::string> opt_string_null;
|
||||
};
|
||||
|
||||
OptionalTypes test;
|
||||
test.opt_int_with_value = 42;
|
||||
test.opt_string_with_value = "optional_test";
|
||||
test.opt_int_null = std::nullopt;
|
||||
test.opt_string_null = std::nullopt;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"opt_int_with_value\":42") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_string_with_value\":\"optional_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_int_null\":null") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_string_null\":null") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<OptionalTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
OptionalTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.opt_int_with_value.has_value());
|
||||
ASSERT_EQUAL(*deserialized.opt_int_with_value, 42);
|
||||
ASSERT_TRUE(deserialized.opt_string_with_value.has_value());
|
||||
ASSERT_EQUAL(*deserialized.opt_string_with_value, "optional_test");
|
||||
ASSERT_FALSE(deserialized.opt_int_null.has_value());
|
||||
ASSERT_FALSE(deserialized.opt_string_null.has_value());
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_smart_pointer_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct SmartPointerTypes {
|
||||
std::unique_ptr<int> unique_int_with_value;
|
||||
std::shared_ptr<std::string> shared_string_with_value;
|
||||
std::unique_ptr<bool> unique_bool_with_value;
|
||||
std::unique_ptr<int> unique_int_null;
|
||||
std::shared_ptr<std::string> shared_string_null;
|
||||
};
|
||||
|
||||
SmartPointerTypes test;
|
||||
test.unique_int_with_value = std::make_unique<int>(123);
|
||||
test.shared_string_with_value = std::make_shared<std::string>("shared_test");
|
||||
test.unique_bool_with_value = std::make_unique<bool>(true);
|
||||
test.unique_int_null = nullptr;
|
||||
test.shared_string_null = nullptr;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"unique_int_with_value\":123") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"shared_string_with_value\":\"shared_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"unique_bool_with_value\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"unique_int_null\":null") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"shared_string_null\":null") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<SmartPointerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
SmartPointerTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.unique_int_with_value != nullptr);
|
||||
ASSERT_EQUAL(*deserialized.unique_int_with_value, 123);
|
||||
ASSERT_TRUE(deserialized.shared_string_with_value != nullptr);
|
||||
ASSERT_EQUAL(*deserialized.shared_string_with_value, "shared_test");
|
||||
ASSERT_TRUE(deserialized.unique_bool_with_value != nullptr);
|
||||
ASSERT_EQUAL(*deserialized.unique_bool_with_value, true);
|
||||
ASSERT_TRUE(deserialized.unique_int_null == nullptr);
|
||||
ASSERT_TRUE(deserialized.shared_string_null == nullptr);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_container_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct ContainerTypes {
|
||||
std::vector<int> int_vector;
|
||||
std::set<std::string> string_set;
|
||||
std::map<std::string, int> string_map;
|
||||
};
|
||||
|
||||
ContainerTypes test;
|
||||
test.int_vector = {1, 2, 3, 4, 5};
|
||||
test.string_set = {"apple", "banana", "cherry"};
|
||||
test.string_map = {{"key1", 10}, {"key2", 20}, {"key3", 30}};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"int_vector\":[1,2,3,4,5]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_set\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_map\":{") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<ContainerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
ContainerTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.int_vector.size(), 5);
|
||||
ASSERT_EQUAL(deserialized.string_set.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.string_map.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.int_vector[0], 1);
|
||||
ASSERT_EQUAL(deserialized.int_vector[4], 5);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_individual_serialization() {
|
||||
TEST_START();
|
||||
|
||||
// Test individual type serialization (not part of structs)
|
||||
auto bool_result = builder::to_json_string(true);
|
||||
ASSERT_SUCCESS(bool_result);
|
||||
ASSERT_EQUAL(bool_result.value(), "true");
|
||||
|
||||
auto int_result = builder::to_json_string(42);
|
||||
ASSERT_SUCCESS(int_result);
|
||||
ASSERT_EQUAL(int_result.value(), "42");
|
||||
|
||||
auto string_result = builder::to_json_string(std::string("hello"));
|
||||
ASSERT_SUCCESS(string_result);
|
||||
ASSERT_EQUAL(string_result.value(), "\"hello\"");
|
||||
|
||||
auto char_result = builder::to_json_string('X');
|
||||
ASSERT_SUCCESS(char_result);
|
||||
ASSERT_EQUAL(char_result.value(), "\"X\"");
|
||||
|
||||
// Test const char* serialization (serialization only, not round-trip)
|
||||
const char* cstring = "cstring";
|
||||
auto cstring_result = builder::to_json_string(cstring);
|
||||
ASSERT_SUCCESS(cstring_result);
|
||||
ASSERT_EQUAL(cstring_result.value(), "\"cstring\"");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_primitive_types() &&
|
||||
test_string_types() &&
|
||||
test_optional_types() &&
|
||||
test_smart_pointer_types() &&
|
||||
test_container_types() &&
|
||||
test_individual_serialization();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, builder_tests::run);
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
bool test_empty_values() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct EmptyValues {
|
||||
std::string empty_string;
|
||||
std::vector<int> empty_vector;
|
||||
std::optional<int> null_optional;
|
||||
std::unique_ptr<int> null_unique_ptr;
|
||||
std::shared_ptr<std::string> null_shared_ptr;
|
||||
};
|
||||
|
||||
EmptyValues test;
|
||||
test.empty_string = "";
|
||||
// empty_vector is already empty by default
|
||||
test.null_optional = std::nullopt;
|
||||
test.null_unique_ptr = nullptr;
|
||||
test.null_shared_ptr = nullptr;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"empty_string\":\"\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"empty_vector\":[]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"null_optional\":null") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"null_unique_ptr\":null") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"null_shared_ptr\":null") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<EmptyValues>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
EmptyValues deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.empty_string, "");
|
||||
ASSERT_EQUAL(deserialized.empty_vector.size(), 0);
|
||||
ASSERT_FALSE(deserialized.null_optional.has_value());
|
||||
ASSERT_TRUE(deserialized.null_unique_ptr == nullptr);
|
||||
ASSERT_TRUE(deserialized.null_shared_ptr == nullptr);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_special_characters() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct SpecialChars {
|
||||
std::string quotes;
|
||||
std::string backslashes;
|
||||
std::string newlines;
|
||||
std::string unicode;
|
||||
char null_char;
|
||||
};
|
||||
|
||||
SpecialChars test;
|
||||
test.quotes = "He said \"Hello\"";
|
||||
test.backslashes = "Path\\to\\file";
|
||||
test.newlines = "Line1\nLine2\tTabbed";
|
||||
test.unicode = "Café résumé";
|
||||
test.null_char = '\0';
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
// Test that quotes are properly escaped
|
||||
ASSERT_TRUE(json.find("\\\"Hello\\\"") != std::string::npos);
|
||||
// Test that backslashes are properly escaped
|
||||
ASSERT_TRUE(json.find("\\\\to\\\\") != std::string::npos);
|
||||
// Test that newlines are properly escaped
|
||||
ASSERT_TRUE(json.find("\\n") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\\t") != std::string::npos);
|
||||
|
||||
// Test round-trip (excluding null char which has special handling)
|
||||
struct SpecialCharsNoNull {
|
||||
std::string quotes;
|
||||
std::string backslashes;
|
||||
std::string newlines;
|
||||
std::string unicode;
|
||||
};
|
||||
|
||||
SpecialCharsNoNull test_no_null;
|
||||
test_no_null.quotes = test.quotes;
|
||||
test_no_null.backslashes = test.backslashes;
|
||||
test_no_null.newlines = test.newlines;
|
||||
test_no_null.unicode = test.unicode;
|
||||
|
||||
auto result_no_null = builder::to_json_string(test_no_null);
|
||||
ASSERT_SUCCESS(result_no_null);
|
||||
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(result_no_null.value()));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<SpecialCharsNoNull>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
SpecialCharsNoNull deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.quotes, test.quotes);
|
||||
ASSERT_EQUAL(deserialized.backslashes, test.backslashes);
|
||||
ASSERT_EQUAL(deserialized.newlines, test.newlines);
|
||||
ASSERT_EQUAL(deserialized.unicode, test.unicode);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_numeric_limits() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct NumericLimits {
|
||||
int max_int;
|
||||
int min_int;
|
||||
double max_double;
|
||||
double min_double;
|
||||
bool true_val;
|
||||
bool false_val;
|
||||
};
|
||||
|
||||
NumericLimits test;
|
||||
test.max_int = std::numeric_limits<int>::max();
|
||||
test.min_int = std::numeric_limits<int>::min();
|
||||
test.max_double = 1e100; // Large but safe double value
|
||||
test.min_double = -1e100; // Large negative but safe double value
|
||||
test.true_val = true;
|
||||
test.false_val = false;
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"true_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"false_val\":false") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<NumericLimits>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
NumericLimits deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.max_int, test.max_int);
|
||||
ASSERT_EQUAL(deserialized.min_int, test.min_int);
|
||||
ASSERT_EQUAL(deserialized.true_val, true);
|
||||
ASSERT_EQUAL(deserialized.false_val, false);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_nested_structures() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct Inner {
|
||||
int value;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct Outer {
|
||||
Inner inner_obj;
|
||||
std::vector<Inner> inner_vector;
|
||||
std::optional<Inner> optional_inner;
|
||||
std::unique_ptr<Inner> unique_inner;
|
||||
};
|
||||
|
||||
Outer test;
|
||||
test.inner_obj = {42, "inner"};
|
||||
test.inner_vector = {{1, "first"}, {2, "second"}};
|
||||
test.optional_inner = Inner{99, "optional"};
|
||||
test.unique_inner = std::make_unique<Inner>(Inner{123, "unique"});
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"inner_obj\":{") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"inner_vector\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"optional_inner\":{") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"unique_inner\":{") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Outer>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Outer deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.inner_obj.value, 42);
|
||||
ASSERT_EQUAL(deserialized.inner_obj.name, "inner");
|
||||
ASSERT_EQUAL(deserialized.inner_vector.size(), 2);
|
||||
ASSERT_EQUAL(deserialized.inner_vector[0].value, 1);
|
||||
ASSERT_EQUAL(deserialized.inner_vector[1].name, "second");
|
||||
ASSERT_TRUE(deserialized.optional_inner.has_value());
|
||||
ASSERT_EQUAL(deserialized.optional_inner->value, 99);
|
||||
ASSERT_TRUE(deserialized.unique_inner != nullptr);
|
||||
ASSERT_EQUAL(deserialized.unique_inner->value, 123);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_const_char_serialization_only() {
|
||||
TEST_START();
|
||||
|
||||
// Test that const char* can be serialized (but not round-tripped)
|
||||
const char* test_cstring = "const char test";
|
||||
auto result = builder::to_json_string(test_cstring);
|
||||
ASSERT_SUCCESS(result);
|
||||
ASSERT_EQUAL(result.value(), "\"const char test\"");
|
||||
|
||||
// Test with special characters in const char*
|
||||
const char* special_cstring = "quotes\"and\\backslashes";
|
||||
auto special_result = builder::to_json_string(special_cstring);
|
||||
ASSERT_SUCCESS(special_result);
|
||||
ASSERT_TRUE(special_result.value().find("\\\"") != std::string::npos);
|
||||
ASSERT_TRUE(special_result.value().find("\\\\") != std::string::npos);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_empty_values() &&
|
||||
test_special_characters() &&
|
||||
test_numeric_limits() &&
|
||||
test_nested_structures() &&
|
||||
test_const_char_serialization_only();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, builder_tests::run);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
bool test_enum_serialization() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
enum class Color {
|
||||
Red,
|
||||
Green,
|
||||
Blue
|
||||
};
|
||||
|
||||
struct EnumStruct {
|
||||
Color color;
|
||||
int value;
|
||||
};
|
||||
|
||||
EnumStruct test{Color::Red, 42};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
// Enum should be serialized as string (Red)
|
||||
ASSERT_TRUE(json.find("\"color\":\"Red\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"value\":42") != std::string::npos);
|
||||
|
||||
// Test different enum values
|
||||
test.color = Color::Green;
|
||||
auto result2 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result2);
|
||||
std::string json2 = result2.value();
|
||||
ASSERT_TRUE(json2.find("\"color\":\"Green\"") != std::string::npos);
|
||||
|
||||
test.color = Color::Blue;
|
||||
auto result3 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result3);
|
||||
std::string json3 = result3.value();
|
||||
ASSERT_TRUE(json3.find("\"color\":\"Blue\"") != std::string::npos);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_enum_deserialization() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
enum class Status {
|
||||
Active,
|
||||
Inactive,
|
||||
Pending
|
||||
};
|
||||
|
||||
struct StatusStruct {
|
||||
Status status;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Test deserialization of different enum values with string representation
|
||||
std::string json1 = "{\"status\":\"Active\",\"name\":\"test1\"}";
|
||||
ondemand::parser parser1;
|
||||
auto doc_result1 = parser1.iterate(pad(json1));
|
||||
ASSERT_SUCCESS(doc_result1);
|
||||
|
||||
auto get_result1 = doc_result1.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result1);
|
||||
|
||||
StatusStruct deserialized1 = std::move(get_result1.value());
|
||||
ASSERT_TRUE(deserialized1.status == Status::Active);
|
||||
ASSERT_EQUAL(deserialized1.name, "test1");
|
||||
|
||||
// Test Status::Inactive
|
||||
std::string json2 = "{\"status\":\"Inactive\",\"name\":\"test2\"}";
|
||||
ondemand::parser parser2;
|
||||
auto doc_result2 = parser2.iterate(pad(json2));
|
||||
ASSERT_SUCCESS(doc_result2);
|
||||
|
||||
auto get_result2 = doc_result2.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result2);
|
||||
|
||||
StatusStruct deserialized2 = std::move(get_result2.value());
|
||||
ASSERT_TRUE(deserialized2.status == Status::Inactive);
|
||||
ASSERT_EQUAL(deserialized2.name, "test2");
|
||||
|
||||
// Test Status::Pending
|
||||
std::string json3 = "{\"status\":\"Pending\",\"name\":\"test3\"}";
|
||||
ondemand::parser parser3;
|
||||
auto doc_result3 = parser3.iterate(pad(json3));
|
||||
ASSERT_SUCCESS(doc_result3);
|
||||
|
||||
auto get_result3 = doc_result3.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result3);
|
||||
|
||||
StatusStruct deserialized3 = std::move(get_result3.value());
|
||||
ASSERT_TRUE(deserialized3.status == Status::Pending);
|
||||
ASSERT_EQUAL(deserialized3.name, "test3");
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_enum_round_trip() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
enum class Priority {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Critical
|
||||
};
|
||||
|
||||
struct Task {
|
||||
Priority priority;
|
||||
std::string description;
|
||||
int id;
|
||||
};
|
||||
|
||||
Task original{Priority::High, "Important task", 123};
|
||||
|
||||
// Serialize
|
||||
auto serialize_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(serialize_result);
|
||||
|
||||
std::string json = serialize_result.value();
|
||||
ASSERT_TRUE(json.find("\"priority\":\"High\"") != std::string::npos); // High as string
|
||||
ASSERT_TRUE(json.find("\"description\":\"Important task\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"id\":123") != std::string::npos);
|
||||
|
||||
// Deserialize
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Task>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Task deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.priority == Priority::High);
|
||||
ASSERT_EQUAL(deserialized.description, "Important task");
|
||||
ASSERT_EQUAL(deserialized.id, 123);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_enum_with_underlying_type() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
enum class ErrorCode : int {
|
||||
Success = 0,
|
||||
NotFound = 404,
|
||||
ServerError = 500
|
||||
};
|
||||
|
||||
struct Response {
|
||||
ErrorCode error;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
Response test{ErrorCode::NotFound, "Resource not found"};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"error\":\"NotFound\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"message\":\"Resource not found\"") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Response>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Response deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.error == ErrorCode::NotFound);
|
||||
ASSERT_EQUAL(deserialized.message, "Resource not found");
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_multiple_enums() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
enum class Day {
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday,
|
||||
Sunday
|
||||
};
|
||||
|
||||
enum class Month {
|
||||
January,
|
||||
February,
|
||||
March,
|
||||
April,
|
||||
May,
|
||||
June,
|
||||
July,
|
||||
August,
|
||||
September,
|
||||
October,
|
||||
November,
|
||||
December
|
||||
};
|
||||
|
||||
struct Date {
|
||||
Day day;
|
||||
Month month;
|
||||
int year;
|
||||
};
|
||||
|
||||
Date test{Day::Friday, Month::July, 2024};
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"day\":\"Friday\"") != std::string::npos); // Friday as string
|
||||
ASSERT_TRUE(json.find("\"month\":\"July\"") != std::string::npos); // July as string
|
||||
ASSERT_TRUE(json.find("\"year\":2024") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Date>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Date deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.day == Day::Friday);
|
||||
ASSERT_TRUE(deserialized.month == Month::July);
|
||||
ASSERT_EQUAL(deserialized.year, 2024);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_enum_serialization() &&
|
||||
test_enum_deserialization() &&
|
||||
test_enum_round_trip() &&
|
||||
test_enum_with_underlying_type() &&
|
||||
test_multiple_enums();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, builder_tests::run);
|
||||
}
|
||||
Reference in New Issue
Block a user