launchdarkly / cpp-sdks

C++ Client/Server SDKs
Other
5 stars 2 forks source link

Serialize/deserialize LDValue from JSON #416

Closed cwaldren-ld closed 2 weeks ago

cwaldren-ld commented 1 month ago

See discussion in this comment: https://github.com/launchdarkly/cpp-sdks/issues/385#issuecomment-2173838016

Customer noted that the SDK is missing serialize/deserialize methods for LDValue. These are available in the C++ SDK, but not as C binding via boost/json.

We should consider adding them to the SDK. Feel free to thumbs up this issue or comment if relevant.

cwaldren-ld commented 1 month ago

Hi @ngangomsamananda , you are correct there is no replacement yet for serializing/deserializing LDValue to JSON in C binding, but it is available in C++.

We could add it, if you require it.

Let me address two issues you bring up.

Issue 1: incorrect defaultValue

I would like to point out in your example, you are using a JSON array (value = "[]";) with LDValue_NewString. Instead, you should be using LDValue_ArrayBuilder (reference).

Example:

#include <launchdarkly/bindings/c/array_builder.h>

// In your function:
LDArrayBuilder array_builder = LDArrayBuilder_New();
LDValue empty_array = LDArrayBuilder_Build(array_builder);

// There is no need to free the LDArrayBuilder if you have called Build, it will be done automatically.

Now if you pass empty_array into LDClientSDK_JsonVariation as the defaultValue, it should be returned properly as an empty array.

Issue 2: incorrect resultValue

In your example, calling LDValue_GetString will only return the correct result if the evaluated result is actually a string. I assume from your example it will be an array.

Depending on your requirements, you can solve this with existing SDK methods, or we may need LDJSONSerialize to be implemented.

For example, if you know the result is always going to be an array, you could use LDValue_ArrayIter (reference):

LDValue_ArrayIter iter = LDValue_ArrayIter_New(resultValue);
while (!LDValue_ArrayIter_End(iter)) {
       // Please note, you should treat value_ref as const*. It is not copied, it is still 
       // owned by the parent resultValue.
       LDValue value_ref = LDValue_ArrayIter_Value(iter);
       // You can do something with the value here.
       handle(value_ref);
       // Advance the iterator.
       LDValue_ArrayIter_Next(iter);
}
LDValue_ArrayIter_Free(iter);

You can do similar with LDValue_ObjectIter (reference) if you know the value is a JSON object. You can always determine the type of a JSON value with LDValue_Type (reference).

If you need the serialized JSON string, for example to pass it on to a web browser or another application, then we need to implement a new C API.

Let me know, and I can file a feature request.

ngangomsamananda commented 1 month ago

Hi @cwaldren-ld, I am able to get the expected result with the inputs you have provided. Serialize/deserialize methods for LDValue will be good features to have in future. Thanks again for all the helps you have provided me.

ngangomsamananda commented 3 weeks ago

@cwaldren-ld I have a query but it's nothing related to this ticket. There is no option to create query tickets. Only bug and feature request are available. I had created a support request but it's not much helpful. I wanted to check with you directly and I don't know where else I can contact you, so I am adding the comment here.

Query In old version(v2.5.2) of SDK, we fall back to Polling mode when Network security software block the SSE communication. For that we check for an error message "streaming failed with recoverable error, backing off". I am trying to find out what error message does sdk sent in log in new SDK(v3.6.0) when there is an SSE communication problem. old sdk link: https://github.com/launchdarkly/c-client-sdk/blob/78f663ae37093f7ec107e1ed5fa4e1eca175c63a/src/ldthreads.c#L585

sample code:

// text - log message from sdk
 if (boost::contains(text, "streaming failed with recoverable error, backing off"))
        {
            bool isInitialized = LDClientSDK_Initialized(g_pLDClient);
            if (!isInitialized)
                switchToPolling = true;
        }
        if (switchToPolling)
        {
            LDDataSourcePollBuilder pollBuilder = LDDataSourcePollBuilder_New();
            if (pollBuilder && g_pLDConfigBuilder)
            {
                LDDataSourcePollBuilder_IntervalS(pollBuilder, POLLING_INTERVAL);
                LDClientConfigBuilder_DataSource_MethodPoll(g_pLDConfigBuilder, pollBuilder);
            }
        }
cwaldren-ld commented 2 weeks ago

@ngangomsamananda I will check the support ticket.