From e0a60edbccb47e855ef7a70fbd86bcfa62d18774 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Fri, 22 Mar 2024 15:38:03 +0100 Subject: [PATCH] feat(value): expose to_json_string Signed-off-by: Tony Gorez --- src/engine/include/includejs/engine_value.h | 1 + src/engine/javascript_core/engine_value.cc | 29 +++++++++++++++ test/engine/CMakeLists.txt | 3 +- .../engine_context_to_json_string_test.cc | 36 +++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/engine/engine_context_to_json_string_test.cc diff --git a/src/engine/include/includejs/engine_value.h b/src/engine/include/includejs/engine_value.h index a875e7a..e6512d7 100644 --- a/src/engine/include/includejs/engine_value.h +++ b/src/engine/include/includejs/engine_value.h @@ -64,6 +64,7 @@ class INCLUDEJS_ENGINE_EXPORT Value { auto push(Value value) -> void; auto to_map() const -> std::map; auto to_vector() const -> std::vector; + auto to_json_string() const -> std::string; // For internal use only #ifndef DOXYGEN diff --git a/src/engine/javascript_core/engine_value.cc b/src/engine/javascript_core/engine_value.cc index 9a1a81a..db3cf08 100644 --- a/src/engine/javascript_core/engine_value.cc +++ b/src/engine/javascript_core/engine_value.cc @@ -436,6 +436,35 @@ auto Value::push(Value value) -> void { assert(!exception); } +auto Value::to_json_string() const -> std::string { + assert(is_object()); + + JSValueRef exception = nullptr; + JSStringRef json_string = JSValueCreateJSONString( + this->internal->context, this->internal->value, 0, &exception); + + if (exception) { + JSStringRef error_message = + JSValueToStringCopy(this->internal->context, exception, nullptr); + JSStringRelease(json_string); + throw std::runtime_error(js_string_to_std_string(error_message)); + } + + if (!json_string) { + JSStringRelease(json_string); + return ""; + } + + try { + std::string result{js_string_to_std_string(json_string)}; + JSStringRelease(json_string); + return result; + } catch (const std::exception &) { + JSStringRelease(json_string); + throw std::runtime_error("Failed to convert to JSON string"); + } +} + auto Value::native() const -> const void * { return static_cast(this->internal->value); } diff --git a/test/engine/CMakeLists.txt b/test/engine/CMakeLists.txt index 409d266..0c80352 100644 --- a/test/engine/CMakeLists.txt +++ b/test/engine/CMakeLists.txt @@ -13,7 +13,8 @@ add_executable(includejs_engine_unit engine_value_undefined_test.cc engine_value_type_test.cc engine_value_null_test.cc - engine_context_from_json_test.cc) + engine_context_from_json_test.cc + engine_context_to_json_string_test.cc) includejs_add_compile_options(includejs_engine_unit) diff --git a/test/engine/engine_context_to_json_string_test.cc b/test/engine/engine_context_to_json_string_test.cc new file mode 100644 index 0000000..111b312 --- /dev/null +++ b/test/engine/engine_context_to_json_string_test.cc @@ -0,0 +1,36 @@ +#include + +#include +#include + +#include + +TEST(IncludeJS_Engine, to_json_string_object) { + includejs::Engine engine; + auto obj = engine.context().make_object(); + obj.set("foo", engine.context().from("bar")); + + auto json_object = obj.to_json_string(); + EXPECT_EQ(json_object, "{\"foo\":\"bar\"}"); +} + +TEST(IncludeJS_Engine, to_json_skip_function_in_object) { + includejs::Engine engine; + auto obj = engine.context().make_object(); + obj.set("bar", [&engine](std::vector) { + return engine.context().from("baz"); + }); + + auto json = obj.to_json_string(); + EXPECT_EQ(json, "{}"); +} + +TEST(IncludeJS_Engine, to_json_string_array) { + includejs::Engine engine; + auto arr = engine.context().make_array(); + arr.push(engine.context().from("foo")); + arr.push(engine.context().from("bar")); + + auto json_array = arr.to_json_string(); + EXPECT_EQ(json_array, "[\"foo\",\"bar\"]"); +}