Skip to content

Commit 90eb880

Browse files
authored
feat: add LDValue_SerializeJSON C binding (#458)
This adds a new API to the `LDValue` interface for serializing to a JSON string. Example: ```c LDValue foo = LDValue_NewBool(true); char *json = LDValue_SerializeJSON(foo); // json is the string 'true' LDMemory_FreeString(json); LDValue_Free(foo); ``` It's easier to view this PR commit to commit. The first one was necessary in order to access the boost::json tag_invoke implementations in `common`, where `LDValue` is implemented. Luckily, I only needed to pull over the `value` headers and some utilities. One thing to note is that boost serializes numbers using scientific notation (e.g. `17` serializes to `1.7E1`). Kind of an odd choice, but it is to JSON spec. BEGIN_COMMIT_OVERRIDE refactor: move json_errors, primitives, and value from internal to common lib feat: add LDValue_SerializeJSON C binding END_COMMIT_OVERRIDE
1 parent cd6bbd0 commit 90eb880

39 files changed

+130
-43
lines changed

contract-tests/client-contract-tests/src/client_entity.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
#include <launchdarkly/context_builder.hpp>
77
#include <launchdarkly/serialization/json_context.hpp>
88
#include <launchdarkly/serialization/json_evaluation_reason.hpp>
9-
#include <launchdarkly/serialization/json_primitives.hpp>
10-
#include <launchdarkly/serialization/json_value.hpp>
9+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
10+
#include <launchdarkly/detail/serialization/json_value.hpp>
1111
#include <launchdarkly/value.hpp>
1212

1313
#include <chrono>

contract-tests/server-contract-tests/src/client_entity.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
#include <launchdarkly/context_builder.hpp>
77
#include <launchdarkly/serialization/json_context.hpp>
88
#include <launchdarkly/serialization/json_evaluation_reason.hpp>
9-
#include <launchdarkly/serialization/json_primitives.hpp>
10-
#include <launchdarkly/serialization/json_value.hpp>
9+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
10+
#include <launchdarkly/detail/serialization/json_value.hpp>
1111
#include <launchdarkly/server_side/serialization/json_all_flags_state.hpp>
1212
#include <launchdarkly/value.hpp>
1313

libs/client-sdk/src/data_sources/data_source_event_handler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <launchdarkly/encoding/base_64.hpp>
44
#include <launchdarkly/serialization/json_evaluation_result.hpp>
55
#include <launchdarkly/serialization/json_item_descriptor.hpp>
6-
#include <launchdarkly/serialization/json_primitives.hpp>
6+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
77
#include <launchdarkly/serialization/value_mapping.hpp>
88

99
#include <boost/core/ignore_unused.hpp>

libs/client-sdk/src/flag_manager/flag_persistence.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#include <launchdarkly/serialization/json_evaluation_result.hpp>
77
#include <launchdarkly/serialization/json_item_descriptor.hpp>
8-
#include <launchdarkly/serialization/json_primitives.hpp>
8+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
99

1010
#include <utility>
1111

libs/common/include/launchdarkly/bindings/c/value.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,21 @@ LDValue_GetString(LDValue val);
191191
LD_EXPORT(unsigned int)
192192
LDValue_Count(LDValue val);
193193

194+
/**
195+
* Serializes the LDValue to a JSON value. The returning string should be
196+
* freed with @ref LDMemory_FreeString.
197+
*
198+
* Please note that numbers are serialized using scientific notation;
199+
* for example the number 17 would be serialized as '1.7E1'.
200+
*
201+
* @param val Target LDValue. Must not be NULL.
202+
* @return A string containing the JSON representation of the LDValue. The
203+
* string should be freed with @ref LDMemory_FreeString.
204+
*
205+
*/
206+
LD_EXPORT(char*)
207+
LDValue_SerializeJSON(LDValue val);
208+
194209
/**
195210
* Obtain iterator over an array-type @ref LDValue, otherwise NULL.
196211
*

libs/internal/include/launchdarkly/serialization/json_primitives.hpp renamed to libs/common/include/launchdarkly/detail/serialization/json_primitives.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
#include <launchdarkly/serialization/json_errors.hpp>
3+
#include <launchdarkly/detail/serialization/json_errors.hpp>
44
#include <tl/expected.hpp>
55

66
#include <boost/core/ignore_unused.hpp>

libs/internal/include/launchdarkly/serialization/json_value.hpp renamed to libs/common/include/launchdarkly/detail/serialization/json_value.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
#include <launchdarkly/serialization/json_errors.hpp>
3+
#include <launchdarkly/detail/serialization/json_errors.hpp>
44
#include <launchdarkly/value.hpp>
55

66
#include <boost/json/fwd.hpp>

libs/common/src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ file(GLOB HEADER_LIST CONFIGURE_DEPENDS
1414
"${LaunchDarklyCommonSdk_SOURCE_DIR}/include/launchdarkly/data/*.hpp"
1515
"${LaunchDarklyCommonSdk_SOURCE_DIR}/include/launchdarkly/logging/*.hpp"
1616
"${LaunchDarklyCommonSdk_SOURCE_DIR}/include/launchdarkly/data_sources/*.hpp"
17+
"${LaunchDarklyCommonSdk_SOURCE_DIR}/include/launchdarkly/data_sources/persistence/*.hpp"
1718
)
1819

1920
# Automatic library: static or dynamic based on user config.
@@ -53,6 +54,8 @@ add_library(${LIBNAME} OBJECT
5354
bindings/c/memory_routines.cpp
5455
bindings/c/data_source/error_info.cpp
5556
bindings/c/logging/log_level.cpp
57+
serialization/json_errors.cpp
58+
serialization/json_value.cpp
5659
log_level.cpp
5760
config/persistence_builder.cpp
5861
config/logging_builder.cpp

libs/common/src/bindings/c/value.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include <launchdarkly/detail/unreachable.hpp>
88
#include <launchdarkly/value.hpp>
99

10+
#include <launchdarkly/detail/serialization/json_value.hpp>
11+
12+
#include <boost/json.hpp>
13+
1014
using launchdarkly::Value;
1115

1216
#define AS_VALUE(x) reinterpret_cast<Value*>(x)
@@ -95,6 +99,18 @@ LD_EXPORT(unsigned int) LDValue_Count(LDValue val) {
9599
}
96100
}
97101

102+
LD_EXPORT(char*) LDValue_SerializeJSON(LDValue val) {
103+
LD_ASSERT_NOT_NULL(val);
104+
105+
auto const value = AS_VALUE(val);
106+
107+
auto boost_value = boost::json::value_from(*value);
108+
109+
std::string json = boost::json::serialize(boost_value);
110+
111+
return strdup(json.c_str());
112+
}
113+
98114
LD_EXPORT(LDValue_ArrayIter) LDValue_ArrayIter_New(LDValue val) {
99115
LD_ASSERT_NOT_NULL(val);
100116

libs/internal/src/serialization/json_errors.cpp renamed to libs/common/src/serialization/json_errors.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <launchdarkly/serialization/json_errors.hpp>
1+
#include <launchdarkly/detail/serialization/json_errors.hpp>
22

33
namespace launchdarkly {
44

libs/internal/src/serialization/json_value.cpp renamed to libs/common/src/serialization/json_value.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
#include <boost/json.hpp>
21
#include <launchdarkly/detail/unreachable.hpp>
3-
#include <launchdarkly/serialization/json_value.hpp>
4-
#include <launchdarkly/serialization/value_mapping.hpp>
2+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
3+
#include <launchdarkly/detail/serialization/json_value.hpp>
4+
5+
#include <boost/json.hpp>
56

67
namespace launchdarkly {
78
// NOLINTBEGIN modernize-return-braced-init-list

libs/common/tests/bindings/value_test.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "launchdarkly/bindings/c/object_builder.h"
55
#include "launchdarkly/bindings/c/value.h"
66

7+
#include "launchdarkly/bindings/c/memory_routines.h"
8+
79
TEST(ValueCBindingTests, CanCreateNull) {
810
auto* ptr = LDValue_NewNull();
911

@@ -168,3 +170,56 @@ TEST(ValueCBindingTests, CanCreateObject) {
168170

169171
LDValue_Free(val_ptr);
170172
}
173+
174+
// Helper to serialize an LDValue, automatically converts to
175+
// std::string and frees the result using LDMemory_FreeString.
176+
std::string serialize(LDValue const val) {
177+
char* serialized = LDValue_SerializeJSON(val);
178+
std::string result(serialized);
179+
LDMemory_FreeString(serialized);
180+
return result;
181+
}
182+
183+
TEST(ValueCBindingTests, CanSerializeToJSON) {
184+
auto* null_val = LDValue_NewNull();
185+
auto* bool_val_true = LDValue_NewBool(true);
186+
auto* bool_val_false = LDValue_NewBool(false);
187+
188+
auto* num_val = LDValue_NewNumber(17);
189+
auto* float_val = LDValue_NewNumber(3.141);
190+
auto* str_val = LDValue_NewString("Potato");
191+
192+
EXPECT_EQ("null", serialize(null_val));
193+
EXPECT_EQ("true", serialize(bool_val_true));
194+
EXPECT_EQ("false", serialize(bool_val_false));
195+
EXPECT_EQ("1.7E1", serialize(num_val));
196+
EXPECT_EQ("3.141E0", serialize(float_val));
197+
EXPECT_EQ("\"Potato\"", serialize(str_val));
198+
199+
// Object builder is going to take care of freeing all the primitives
200+
// (except for bool_val_false.)
201+
auto* object_builder = LDObjectBuilder_New();
202+
LDObjectBuilder_Add(object_builder, "null", null_val);
203+
LDObjectBuilder_Add(object_builder, "bool", bool_val_true);
204+
LDObjectBuilder_Add(object_builder, "num", num_val);
205+
LDObjectBuilder_Add(object_builder, "float", float_val);
206+
LDObjectBuilder_Add(object_builder, "str", str_val);
207+
208+
auto* obj_ptr = LDObjectBuilder_Build(object_builder);
209+
210+
EXPECT_EQ(
211+
"{\"bool\":true,\"float\":3.141E0,\"null\":null,\"num\":1.7E1,\"str\":"
212+
"\"Potato\"}",
213+
serialize(obj_ptr));
214+
215+
LDValue_Free(obj_ptr);
216+
217+
// Array builder is going to take care of freeing bool_val_false.
218+
auto* array_builder = LDArrayBuilder_New();
219+
LDArrayBuilder_Add(array_builder, bool_val_false);
220+
auto* array_ptr = LDArrayBuilder_Build(array_builder);
221+
222+
EXPECT_EQ("[false]", serialize(array_ptr));
223+
224+
LDValue_Free(array_ptr);
225+
}

libs/common/tests/value_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <gtest/gtest.h>
22

3-
#include <launchdarkly/serialization/json_value.hpp>
3+
#include <launchdarkly/detail/serialization/json_value.hpp>
44

55
#include <boost/json.hpp>
66

libs/internal/include/launchdarkly/serialization/json_context.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/context.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77
#include <tl/expected.hpp>

libs/internal/include/launchdarkly/serialization/json_context_aware_reference.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <launchdarkly/attribute_reference.hpp>
44
#include <launchdarkly/data_model/context_aware_reference.hpp>
55
#include <launchdarkly/serialization/json_context_kind.hpp>
6-
#include <launchdarkly/serialization/json_errors.hpp>
6+
#include <launchdarkly/detail/serialization/json_errors.hpp>
77
#include <launchdarkly/serialization/value_mapping.hpp>
88

99
#include <boost/json.hpp>

libs/internal/include/launchdarkly/serialization/json_context_kind.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/context_kind.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json.hpp>
77
#include <tl/expected.hpp>

libs/internal/include/launchdarkly/serialization/json_evaluation_reason.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data/evaluation_reason.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77
#include <tl/expected.hpp>

libs/internal/include/launchdarkly/serialization/json_evaluation_result.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data/evaluation_result.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77
#include <tl/expected.hpp>

libs/internal/include/launchdarkly/serialization/json_flag.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/flag.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77

libs/internal/include/launchdarkly/serialization/json_item_descriptor.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/item_descriptor.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
5-
#include <launchdarkly/serialization/json_primitives.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
5+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
66
#include <launchdarkly/serialization/value_mapping.hpp>
77

88
#include <boost/json.hpp>

libs/internal/include/launchdarkly/serialization/json_rule_clause.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/rule_clause.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77
#include <tl/expected.hpp>

libs/internal/include/launchdarkly/serialization/json_sdk_data_set.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/sdk_data_set.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77

libs/internal/include/launchdarkly/serialization/json_segment.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <launchdarkly/data_model/segment.hpp>
4-
#include <launchdarkly/serialization/json_errors.hpp>
4+
#include <launchdarkly/detail/serialization/json_errors.hpp>
55

66
#include <boost/json/fwd.hpp>
77

libs/internal/include/launchdarkly/serialization/value_mapping.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#pragma once
22

3-
#include <launchdarkly/serialization/json_errors.hpp>
4-
#include <launchdarkly/serialization/json_primitives.hpp>
3+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
54

65
#include <boost/core/ignore_unused.hpp>
76
#include <boost/json.hpp>

libs/internal/src/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ add_library(${LIBNAME} OBJECT
3030
serialization/events/json_events.cpp
3131
serialization/json_attributes.cpp
3232
serialization/json_context.cpp
33-
serialization/json_errors.cpp
3433
serialization/json_evaluation_reason.cpp
35-
serialization/json_value.cpp
3634
serialization/value_mapping.cpp
3735
serialization/json_evaluation_result.cpp
3836
serialization/json_sdk_data_set.cpp

libs/internal/src/serialization/events/json_events.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <launchdarkly/serialization/events/json_events.hpp>
22
#include <launchdarkly/serialization/json_evaluation_reason.hpp>
3-
#include <launchdarkly/serialization/json_value.hpp>
3+
#include <launchdarkly/detail/serialization/json_value.hpp>
44

55
namespace launchdarkly::events {
66
void tag_invoke(boost::json::value_from_tag const& tag,

libs/internal/src/serialization/json_attributes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include <launchdarkly/serialization/json_attributes.hpp>
2-
#include <launchdarkly/serialization/json_value.hpp>
2+
#include <launchdarkly/detail/serialization/json_value.hpp>
33

44
#include <boost/core/ignore_unused.hpp>
55
#include <boost/json.hpp>

libs/internal/src/serialization/json_context.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include <launchdarkly/context_builder.hpp>
2+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
3+
#include <launchdarkly/detail/serialization/json_value.hpp>
24
#include <launchdarkly/serialization/json_attributes.hpp>
35
#include <launchdarkly/serialization/json_context.hpp>
4-
#include <launchdarkly/serialization/json_primitives.hpp>
5-
#include <launchdarkly/serialization/json_value.hpp>
66

77
#include <boost/core/ignore_unused.hpp>
88
#include <boost/json.hpp>

libs/internal/src/serialization/json_evaluation_reason.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <launchdarkly/serialization/json_errors.hpp>
1+
#include <launchdarkly/detail/serialization/json_errors.hpp>
22
#include <launchdarkly/serialization/json_evaluation_reason.hpp>
33
#include <launchdarkly/serialization/value_mapping.hpp>
44

libs/internal/src/serialization/json_evaluation_result.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
#include <launchdarkly/serialization/json_errors.hpp>
1+
#include <launchdarkly/detail/serialization/json_errors.hpp>
22
#include <launchdarkly/serialization/json_evaluation_reason.hpp>
33
#include <launchdarkly/serialization/json_evaluation_result.hpp>
4-
#include <launchdarkly/serialization/json_value.hpp>
4+
#include <launchdarkly/detail/serialization/json_value.hpp>
55
#include <launchdarkly/serialization/value_mapping.hpp>
66

77
#include <boost/core/ignore_unused.hpp>

libs/internal/src/serialization/json_flag.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <launchdarkly/serialization/json_context_aware_reference.hpp>
33
#include <launchdarkly/serialization/json_flag.hpp>
44
#include <launchdarkly/serialization/json_rule_clause.hpp>
5-
#include <launchdarkly/serialization/json_value.hpp>
5+
#include <launchdarkly/detail/serialization/json_value.hpp>
66
#include <launchdarkly/serialization/value_mapping.hpp>
77

88
namespace launchdarkly {

libs/internal/src/serialization/json_primitives.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <launchdarkly/serialization/json_primitives.hpp>
1+
#include <launchdarkly/detail/serialization/json_primitives.hpp>
22

33
namespace launchdarkly {
44
tl::expected<std::optional<bool>, JsonError> tag_invoke(

libs/internal/src/serialization/json_rule_clause.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <boost/json.hpp>
33
#include <launchdarkly/serialization/json_context_aware_reference.hpp>
44
#include <launchdarkly/serialization/json_rule_clause.hpp>
5-
#include <launchdarkly/serialization/json_value.hpp>
5+
#include <launchdarkly/detail/serialization/json_value.hpp>
66
#include <launchdarkly/serialization/value_mapping.hpp>
77

88
namespace launchdarkly {

0 commit comments

Comments
 (0)