1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-05-22 10:15:16 +02:00

Improve JSON to Variant conversion and add Variant to JSON conversion.

Add boolean and one-dimensional list types to jsonToKv().

Add varToJson() and kvToJson() to convert Variants and KeyValues to JSON.

Contributed by Cynthia Shang.
This commit is contained in:
Cynthia Shang
2018-11-23 16:02:33 -05:00
committed by David Steele
parent e641c130d3
commit f4a1751abc
5 changed files with 661 additions and 19 deletions
+8
View File
@@ -35,6 +35,14 @@
<p>Add interface objects for <proper>libxml2</proper>.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>Improve JSON to <code>Variant</code> conversion and add <code>Variant</code> to JSON conversion.</p>
</release-item>
<release-item>
<p>Allow I/O read interface to explicitly request blocking reads.</p>
</release-item>
+400 -17
View File
@@ -7,6 +7,81 @@ Convert JSON to/from KeyValue
#include "common/log.h"
#include "common/type/json.h"
/***********************************************************************************************************************************
Given a character array and its size, return a variant from the extracted string
***********************************************************************************************************************************/
static Variant *
jsonString(const char *jsonC, size_t strSize, unsigned int *jsonPos, unsigned int *valueBeginPos)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(CHARPY, jsonC);
FUNCTION_TEST_PARAM(SIZE, strSize);
FUNCTION_TEST_PARAM(UINTP, jsonPos);
FUNCTION_TEST_PARAM(UINTP, valueBeginPos);
FUNCTION_TEST_END();
Variant *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
*valueBeginPos = *valueBeginPos + 1;
*jsonPos = *jsonPos + 1;
// Find the end of the string within the entire character array
while (jsonC[*jsonPos] != '"' && *jsonPos < strSize - 1)
*jsonPos = *jsonPos + 1;
if (jsonC[*jsonPos] != '"')
THROW_FMT(JsonFormatError, "expected '\"' but found '%c'", jsonC[*jsonPos]);
// Extract the string, including the enclosing quotes and return it as a variant
String *resultStr = strNewN(jsonC + *valueBeginPos, *jsonPos - *valueBeginPos);
memContextSwitch(MEM_CONTEXT_OLD());
result = varNewStr(resultStr);
memContextSwitch(MEM_CONTEXT_TEMP());
// Advance the character array pointer to the next element after the string
*jsonPos = *jsonPos + 1;
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RESULT(VARIANT, result);
}
/***********************************************************************************************************************************
Given a character array and its size, return a variant from the extracted numeric
***********************************************************************************************************************************/
static Variant *
jsonNumeric(const char *jsonC, size_t strSize, unsigned int *jsonPos, unsigned int *valueBeginPos)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(CHARPY, jsonC);
FUNCTION_TEST_PARAM(SIZE, strSize);
FUNCTION_TEST_PARAM(UINTP, jsonPos);
FUNCTION_TEST_PARAM(UINTP, valueBeginPos);
FUNCTION_TEST_END();
Variant *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Find the end of the numeric within the entire character array
while (isdigit(jsonC[*jsonPos]) && *jsonPos < strSize - 1)
*jsonPos = *jsonPos + 1;
// Extract the numeric as a string
String *resultStr = strNewN(jsonC + *valueBeginPos, *jsonPos - *valueBeginPos);
// Convert the string to a uint64 variant
memContextSwitch(MEM_CONTEXT_OLD());
result = varNewUInt64(cvtZToUInt64(strPtr(resultStr)));
memContextSwitch(MEM_CONTEXT_TEMP());
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RESULT(VARIANT, result);
}
/***********************************************************************************************************************************
Convert JSON to KeyValue object
@@ -14,7 +89,8 @@ Currently this function is only intended to convert the limited types that are i
needed. Since this function is only intended to read internally-generated JSON it is assumed to be well-formed with no extraneous
whitespace.
***********************************************************************************************************************************/
KeyValue *jsonToKv(const String *json)
KeyValue *
jsonToKv(const String *json)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(STRING, json);
@@ -68,32 +144,88 @@ KeyValue *jsonToKv(const String *json)
// The value appears to be a string
if (jsonC[jsonPos] == '"')
{
valueBeginPos++;
jsonPos++;
while (jsonC[jsonPos] != '"' && jsonPos < strSize(json) - 1)
jsonPos++;
if (jsonC[jsonPos] != '"')
THROW_FMT(JsonFormatError, "expected '\"' but found '%c'", jsonC[jsonPos]);
value = varNewStr(strNewN(jsonC + valueBeginPos, jsonPos - valueBeginPos));
jsonPos++;
value = jsonString(jsonC, strSize(json), &jsonPos, &valueBeginPos);
}
// The value appears to be a number
else if (isdigit(jsonC[jsonPos]))
{
while (isdigit(jsonC[jsonPos]) && jsonPos < strSize(json) - 1)
value = jsonNumeric(jsonC, strSize(json), &jsonPos, &valueBeginPos);
}
// The value appears to be a boolean
else if (jsonC[jsonPos] == 't' || jsonC[jsonPos] == 'f')
{
valueBeginPos = jsonPos;
while (jsonC[jsonPos] != 'e' && jsonPos < strSize(json) - 1)
jsonPos++;
if (jsonC[jsonPos] != 'e')
THROW_FMT(JsonFormatError, "expected boolean but found '%c'", jsonC[jsonPos]);
jsonPos++;
String *valueStr = strNewN(jsonC + valueBeginPos, jsonPos - valueBeginPos);
value = varNewUInt64(cvtZToUInt64(strPtr(valueStr)));
if (strCmpZ(valueStr, "true") != 0 && strCmpZ(valueStr, "false") != 0)
THROW_FMT(JsonFormatError, "expected 'true' or 'false' but found '%s'", strPtr(valueStr));
value = varNewBool(varBoolForce(varNewStr(valueStr)));
}
// Else not sure what it is. Currently booleans and nulls will error.
// The value appears to be an array.
else if (jsonC[jsonPos] == '[')
{
// Add a pointer to an empty variant list as the value for the key.
Variant *valueList = varNewVarLst(varLstNew());
kvAdd(result, varNewStr(key), valueList);
// ??? Currently only working with same-type simple single-dimensional arrays
unsigned char arrayType = '\0';
do
{
jsonPos++;
valueBeginPos = jsonPos;
// The value appears to be a string
if (jsonC[jsonPos] == '"')
{
if (arrayType != '\0' && arrayType != 's')
THROW_FMT(JsonFormatError, "string found in array of type '%c'", arrayType);
arrayType = 's';
value = jsonString(jsonC, strSize(json), &jsonPos, &valueBeginPos);
}
// The value appears to be a number
else if (isdigit(jsonC[jsonPos]))
{
if (arrayType != '\0' && arrayType != 'n')
THROW_FMT(JsonFormatError, "number found in array of type '%c'", arrayType);
arrayType = 'n';
value = jsonNumeric(jsonC, strSize(json), &jsonPos, &valueBeginPos);
}
else
THROW(JsonFormatError, "unknown array value type");
kvAdd(result, varNewStr(key), value);
}
while (jsonC[jsonPos] == ',');
if (jsonC[jsonPos] != ']')
THROW_FMT(JsonFormatError, "expected array delimeter ']' but found '%c'", jsonC[jsonPos]);
jsonPos++;
continue;
}
// Else not sure what it is. Currently nulls will error.
else
THROW(JsonFormatError, "unknown value type");
@@ -103,9 +235,260 @@ KeyValue *jsonToKv(const String *json)
// Look for end delimiter
if (jsonC[jsonPos] != '}')
THROW_FMT(JsonFormatError, "expected '}' but found '%c'", jsonC[jsonPos]);
THROW_FMT(JsonFormatError, "expected '}' but found '%c'", jsonC[jsonPos]);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_DEBUG_RESULT(KEY_VALUE, result);
}
/***********************************************************************************************************************************
Internal recursive function to walk a KeyValue and return a json string
***********************************************************************************************************************************/
static String *
kvToJsonInternal(const KeyValue *kv, String *indentSpace, String *indentDepth)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(KEY_VALUE, kv);
FUNCTION_TEST_PARAM(STRING, indentSpace);
FUNCTION_TEST_PARAM(STRING, indentDepth);
FUNCTION_TEST_ASSERT(kv != NULL);
FUNCTION_TEST_ASSERT(indentSpace != NULL);
FUNCTION_TEST_ASSERT(indentDepth != NULL);
FUNCTION_TEST_END();
String *result = strNew("{");
MEM_CONTEXT_TEMP_BEGIN()
{
const StringList *keyList = strLstSort(strLstNewVarLst(kvKeyList(kv)), sortOrderAsc);
// If not an empty list, then add indent formatting
if (strLstSize(keyList) > 0)
strCatFmt(result, "%s", strPtr(indentDepth));
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
{
String *key = strLstGet(keyList, keyIdx);
const Variant *value = kvGet(kv, varNewStr(key));
// If going to add another key, prepend a comma
if (keyIdx > 0)
strCatFmt(result, ",%s", strPtr(indentDepth));
// Keys are always strings in the output, so add starting quote and colon.
// Note if indent is 0 then spaces do not surround the colon, else they do.
if (strSize(indentSpace) == 0)
strCatFmt(result, "\"%s\":", strPtr(key));
else
strCatFmt(result, "\"%s\" : ", strPtr(key));
// NULL value
if (value == NULL)
strCat(result, "null");
// KeyValue
else if (varType(value) == varTypeKeyValue)
{
strCat(indentDepth, strPtr(indentSpace));
strCat(result, strPtr(kvToJsonInternal(kvDup(varKv(value)), indentSpace, indentDepth)));
}
// VariantList
else if (varType(value) == varTypeVariantList)
{
// If the array is empty, then do not add formatting, else process the array.
if (varLstSize(varVarLst(value)) == 0)
strCat(result, "[]");
else
{
strCat(indentDepth, strPtr(indentSpace));
strCatFmt(result, "[%s", strPtr(indentDepth));
for (unsigned int arrayIdx = 0; arrayIdx < varLstSize(varVarLst(value)); arrayIdx++)
{
Variant *arrayValue = varLstGet(varVarLst(value), arrayIdx);
unsigned int type = varType(varLstGet(varVarLst(value), arrayIdx));
// If going to add another element, add a comma
if (arrayIdx > 0)
strCatFmt(result, ",%s", strPtr(indentDepth));
// If the type is a string, add leading and trailing double quotes
if (type == varTypeString)
strCatFmt(result, "\"%s\"", strPtr(varStr(arrayValue)));
else if (type == varTypeKeyValue)
{
strCat(indentDepth, strPtr(indentSpace));
strCat(result, strPtr(kvToJsonInternal(kvDup(varKv(arrayValue)), indentSpace, indentDepth)));
}
// Numeric, Boolean or other type
else
strCat(result, strPtr(varStrForce(arrayValue)));
}
if (strSize(indentDepth) > strSize(indentSpace))
strTrunc(indentDepth, (int)(strSize(indentDepth) - strSize(indentSpace)));
strCatFmt(result, "%s]", strPtr(indentDepth));
}
}
// String
else if (varType(value) == varTypeString)
strCatFmt(result, "\"%s\"", strPtr(varStr(value)));
// Numeric, Boolean or other type
else
strCat(result, strPtr(varStrForce(value)));
}
if (strSize(indentDepth) > strSize(indentSpace))
strTrunc(indentDepth, (int)(strSize(indentDepth) - strSize(indentSpace)));
if (strLstSize(keyList) > 0)
strCatFmt(result, "%s}", strPtr(indentDepth));
else
result = strCat(result, "}");
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RESULT(STRING, result);
}
/***********************************************************************************************************************************
Convert KeyValue object to JSON string. If indent = 0 then no pretty format.
Currently this function is only intended to convert the limited types that are included in info files. More types will be added as
needed. Since this function is only intended to read internally-generated JSON it is assumed to be well-formed with no extraneous
whitespace.
***********************************************************************************************************************************/
String *
kvToJson(const KeyValue *kv, unsigned int indent)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(KEY_VALUE, kv);
FUNCTION_DEBUG_PARAM(UINT, indent);
FUNCTION_DEBUG_ASSERT(kv != NULL);
FUNCTION_DEBUG_END();
String *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
String *jsonStr = strNew("");
String *indentSpace = strNew("");
String *indentDepth = strNew("");
// Set up the indent spacing (indent 0 will result in an empty string)
for (unsigned int indentIdx = 0; indentIdx < indent; indentIdx++)
strCat(indentSpace, " ");
// If indent > 0 (pretty printing) then add carriage return to the indent format
if (indent > 0)
strCat(indentDepth, "\n");
strCat(indentDepth, strPtr(indentSpace));
strCat(jsonStr, strPtr(kvToJsonInternal(kv, indentSpace, indentDepth)));
// Add terminating linefeed for pretty print if it is not already added
if (indent > 0 && !strEndsWithZ(jsonStr, "\n"))
strCat(jsonStr, "\n");
// Duplicate the string into the calling context
memContextSwitch(MEM_CONTEXT_OLD());
result = strDup(jsonStr);
memContextSwitch(MEM_CONTEXT_TEMP());
}
MEM_CONTEXT_TEMP_END();
FUNCTION_DEBUG_RESULT(STRING, result);
}
/***********************************************************************************************************************************
Convert Variant object to JSON string. If indent = 0 then no pretty format.
Currently this function is only intended to convert the limited types that are included in info files. More types will be added as
needed.
***********************************************************************************************************************************/
String *
varToJson(const Variant *var, unsigned int indent)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(VARIANT, var);
FUNCTION_DEBUG_PARAM(UINT, indent);
FUNCTION_DEBUG_ASSERT(var != NULL);
FUNCTION_DEBUG_END();
String *result = NULL;
// Currently the variant to parse must be either a VariantList or a KeyValue type.
if (varType(var) != varTypeVariantList && varType(var) != varTypeKeyValue)
THROW(JsonFormatError, "variant type is invalid");
MEM_CONTEXT_TEMP_BEGIN()
{
String *jsonStr = strNew("");
String *indentSpace = strNew("");
String *indentDepth = strNew("");
// Set up the indent spacing (indent 0 will result in an empty string)
for (unsigned int indentIdx = 0; indentIdx < indent; indentIdx++)
strCat(indentSpace, " ");
// If indent > 0 (pretty printing) then add carriage return to the indent format
if (indent > 0)
strCat(indentDepth, "\n");
strCat(indentDepth, strPtr(indentSpace));
// If VariantList then process each item in the array. Currently the list must be KeyValue types.
if (varType(var) == varTypeVariantList)
{
const VariantList *vl = varVarLst(var);
// If not an empty array
if (varLstSize(vl) > 0)
{
// Add the indent formatting
strCatFmt(jsonStr, "[%s", strPtr(indentDepth));
// Currently only KeyValue list is supported
for (unsigned int vlIdx = 0; vlIdx < varLstSize(vl); vlIdx++)
{
// If going to add another key, append a comma and format for the next line
if (vlIdx > 0)
strCatFmt(jsonStr, ",%s", strPtr(indentDepth));
// Update the depth before processing the contents of the list element
strCat(indentDepth, strPtr(indentSpace));
strCat(jsonStr, strPtr(kvToJsonInternal(varKv(varLstGet(vl, vlIdx)), indentSpace, indentDepth)));
}
// Decrease the depth
if (strSize(indentDepth) > strSize(indentSpace))
strTrunc(indentDepth, (int)(strSize(indentDepth) - strSize(indentSpace)));
// Close the array
strCatFmt(jsonStr, "%s]", strPtr(indentDepth));
}
// Else empty array
else
strCat(jsonStr, "[]");
}
// Else just convert the KeyValue
else
strCat(jsonStr, strPtr(kvToJsonInternal(varKv(var), indentSpace, indentDepth)));
// Add terminating linefeed for pretty print if it is not already added
if (indent > 0 && !strEndsWithZ(jsonStr, "\n"))
strCat(jsonStr, "\n");
// Duplicate the string into the calling context
memContextSwitch(MEM_CONTEXT_OLD());
result = strDup(jsonStr);
memContextSwitch(MEM_CONTEXT_TEMP());
}
MEM_CONTEXT_TEMP_END();
FUNCTION_DEBUG_RESULT(STRING, result);
}
+3
View File
@@ -11,4 +11,7 @@ Functions
***********************************************************************************************************************************/
KeyValue *jsonToKv(const String *json);
String *kvToJson(const KeyValue *kv, unsigned int indent);
String *varToJson(const Variant *var, unsigned int indent);
#endif
+1 -1
View File
@@ -190,7 +190,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: type-json
total: 1
total: 3
coverage:
common/type/json: full
+249 -1
View File
@@ -19,10 +19,20 @@ testRun(void)
TEST_ERROR(jsonToKv(strNew("{\"\"")), JsonFormatError, "zero-length key not allowed");
TEST_ERROR(jsonToKv(strNew("{\"key1\"")), JsonFormatError, "expected ':' but found '");
TEST_ERROR(jsonToKv(strNew("{\"key1'")), JsonFormatError, "expected '\"' but found '''");
TEST_ERROR(jsonToKv(strNew("{\"key1\":t")), JsonFormatError, "unknown value type");
TEST_ERROR(jsonToKv(strNew("{\"key1\":x")), JsonFormatError, "unknown value type");
TEST_ERROR(jsonToKv(strNew("{\"key1\":t")), JsonFormatError, "expected boolean but found 't'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":toe")), JsonFormatError, "expected 'true' or 'false' but found 'toe'");
TEST_ERROR(jsonToKv(strNew("{\"checksum\":\"6721d92c9fcdf4248acff1f9a13\",\"master\":falsehood,\"reference\":\"backup\"}")),
JsonFormatError, "expected '}' but found 'h'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":123")), JsonFormatError, "expected '}' but found '3'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":123>")), JsonFormatError, "expected '}' but found '>'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":\"123'}")), JsonFormatError, "expected '\"' but found '}'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":[\"\",]}")), JsonFormatError, "unknown array value type");
TEST_ERROR(jsonToKv(strNew("{\"key1\":[\"\",1]}")), JsonFormatError, "number found in array of type 's'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":[1,\"\"]}")), JsonFormatError, "string found in array of type 'n'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":[1}")), JsonFormatError, "expected array delimeter ']' but found '}'");
TEST_ERROR(jsonToKv(strNew("{\"key1\":[]}")), JsonFormatError, "unknown array value type");
TEST_ERROR(jsonToKv(strNew("{}")), JsonFormatError, "expected '\"' but found '}'");
// -------------------------------------------------------------------------------------------------------------------------
KeyValue *kv = NULL;
@@ -33,6 +43,12 @@ testRun(void)
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":\"value1\"}")), "single string value");
TEST_RESULT_STR(strPtr(varStr(kvGet(kv, varNewStr(strNew("key1"))))), "value1", " check string");
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":true}")), "boolean true");
TEST_RESULT_BOOL(varBool(kvGet(kv, varNewStr(strNew("key1")))), true, " check boolean");
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":false}")), "boolean false");
TEST_RESULT_BOOL(varBool(kvGet(kv, varNewStr(strNew("key1")))), false, " check boolean");
TEST_ASSIGN(
kv,
jsonToKv(
@@ -44,6 +60,238 @@ testRun(void)
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("db-control-version")))), 942, " check integer");
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("db-system-id")))), 6116111691796124355, " check integer");
TEST_RESULT_STR(strPtr(varStr(kvGet(kv, varNewStr(strNew("db-version"))))), "9.4", " check string");
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":[\"\"]}")), "single-dimension empty string array");
TEST_RESULT_STR(strPtr(varStr(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("key1")))), 0))), "", " array[0]");
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":[\"12AB\",\"34-EF\"]}")), "single-dimension string array");
TEST_RESULT_STR(strPtr(varStr(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("key1")))), 0))), "12AB", " array[0]");
TEST_RESULT_STR(strPtr(varStr(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("key1")))), 1))), "34-EF", " array[1]");
TEST_ASSIGN(kv, jsonToKv(strNew("{\"key1\":[12,34]}")), "single-dimension number array");
TEST_RESULT_UINT(varUInt64(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("key1")))), 0)), 12, " array[0]");
TEST_RESULT_UINT(varUInt64(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("key1")))), 1)), 34, " array[1]");
TEST_ASSIGN(
kv,
jsonToKv(
strNew(
"{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"checksum-page-error\":[1],\"backup-timestamp-start\":1482182951}")),
"multpile values with array");
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("backup-info-size-delta")))), 1982702, " check integer");
TEST_RESULT_STR(strPtr(varStr(kvGet(kv, varNewStr(strNew("backup-prior"))))), "20161219-212741F_20161219-212803I",
" check string");
TEST_RESULT_STR(strPtr(varStr(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("backup-reference")))), 0))),
"20161219-212741F", " check string array[0]");
TEST_RESULT_STR(strPtr(varStr(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("backup-reference")))), 1))),
"20161219-212741F_20161219-212803I", " check string array[1]");
TEST_RESULT_UINT(varUInt64(varLstGet(varVarLst(kvGet(kv, varNewStr(strNew("checksum-page-error")))), 0)), 1,
" check integer array[0]");
TEST_RESULT_UINT(varUInt64(kvGet(kv, varNewStr(strNew("backup-timestamp-start")))), 1482182951, " check integer");
}
// *****************************************************************************************************************************
if (testBegin("kvToJson(), kvToJsonInternal()"))
{
KeyValue *keyValue = kvNew();
String *json = NULL;
TEST_ASSIGN(json, kvToJson(keyValue, 0), "kvToJson - empty, no indent");
TEST_RESULT_STR(strPtr(json), "{}", " empty curly brackets");
TEST_ASSIGN(json, kvToJson(keyValue, 2), "kvToJson - empty, indent 2");
TEST_RESULT_STR(strPtr(json), "{}\n", " empty curly brackets with carriage return");
kvPut(keyValue, varNewStrZ("backup"), varNewVarLst(varLstNew()));
TEST_ASSIGN(json, kvToJson(keyValue, 0), "kvToJson - kv with empty array, no indent");
TEST_RESULT_STR(strPtr(json), "{\"backup\":[]}", " kv with empty array brackets");
kvPut(keyValue, varNewStrZ("backup"), varNewVarLst(varLstNew()));
TEST_ASSIGN(json, kvToJson(keyValue, 2), "kvToJson - kv with empty array, indent 2");
TEST_RESULT_STR(strPtr(json),
"{\n"
" \"backup\" : []\n"
"}\n", " formatted kv with empty array brackets");
kvPutKv(keyValue, varNewStr(strNew("archive")));
kvPutKv(keyValue, varNewStr(strNew("empty")));
kvPut(keyValue, varNewStrZ("bool1"), varNewBool(true));
kvPut(keyValue, varNewStrZ("bool2"), varNewBool(false));
kvPut(keyValue, varNewStrZ("checknull"), (Variant *)NULL);
VariantList *dbList = varLstNew();
Variant *dbInfo = varNewKv();
kvPut(varKv(dbInfo), varNewStr(strNew("id")), varNewInt(1));
kvPut(varKv(dbInfo), varNewStr(strNew("version")), varNewStr(strNew("9.4")));
varLstAdd(dbList, dbInfo);
kvPut(keyValue, varNewStrZ("db"), varNewVarLst(dbList));
TEST_ASSIGN(json, kvToJson(keyValue, 2), "kvToJson - kv with empty array, indent 2");
TEST_RESULT_STR(strPtr(json),
"{\n"
" \"archive\" : {},\n"
" \"backup\" : [],\n"
" \"bool1\" : true,\n"
" \"bool2\" : false,\n"
" \"checknull\" : null,\n"
" \"db\" : [\n"
" {\n"
" \"id\" : 1,\n"
" \"version\" : \"9.4\"\n"
" }\n"
" ],\n"
" \"empty\" : {}\n"
"}\n", " formatted kv with empty array, kv, varList with values");
TEST_ASSIGN(
keyValue,
jsonToKv(
strNew(
"{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"checksum-page-error\":[1],\"backup-timestamp-start\":1482182951}")),
"multpile values with array");
TEST_ASSIGN(json, kvToJson(keyValue, 0), " kvToJson - sorted, no indent");
TEST_RESULT_STR(strPtr(json),
"{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"backup-timestamp-start\":1482182951,\"checksum-page-error\":[1]}",
" check string no pretty print");
}
// *****************************************************************************************************************************
if (testBegin("varToJson()"))
{
TEST_ERROR(varToJson(varNewUInt64(100), 0), JsonFormatError, "variant type is invalid");
String *json = NULL;
Variant *keyValue = NULL;
TEST_ASSIGN(keyValue, varNewKv(), "build new kv");
kvPut(varKv(keyValue), varNewStrZ("backup-info-size-delta"), varNewInt(1982702));
kvPut(varKv(keyValue), varNewStrZ("backup-prior"), varNewStrZ("20161219-212741F_20161219-212803I"));
Variant *listVar = NULL;
TEST_ASSIGN(listVar, varNewVarLst(varLstNew()), " new string array to kv");
varLstAdd(varVarLst(listVar), varNewStrZ("20161219-212741F"));
varLstAdd(varVarLst(listVar), varNewStrZ("20161219-212741F_20161219-212803I"));
kvPut(varKv(keyValue), varNewStrZ("backup-reference"), listVar);
kvPut(varKv(keyValue), varNewStrZ("backup-timestamp-start"), varNewInt(1482182951));
Variant *listVar2 = NULL;
TEST_ASSIGN(listVar2, varNewVarLst(varLstNew()), " new int array to kv");
varLstAdd(varVarLst(listVar2), varNewInt(1));
kvPut(varKv(keyValue), varNewStrZ("checksum-page-error"), listVar2);
// Embed a keyValue section to test recursion
Variant *sectionKey = varNewStr(strNew("section"));
KeyValue *sectionKv = kvPutKv(varKv(keyValue), sectionKey);
kvAdd(sectionKv, varNewStr(strNew("key1")), varNewStr(strNew("value1")));
kvAdd(sectionKv, varNewStr(strNew("key2")), (Variant *)NULL);
kvAdd(sectionKv, varNewStr(strNew("key3")), varNewStr(strNew("value2")));
TEST_ASSIGN(json, varToJson(keyValue, 0), "KeyValue no indent");
TEST_RESULT_STR(strPtr(json),
"{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"backup-timestamp-start\":1482182951,\"checksum-page-error\":[1],"
"\"section\":{\"key1\":\"value1\",\"key2\":null,\"key3\":\"value2\"}}",
" sorted json string result, no pretty print");
TEST_ASSIGN(json, varToJson(keyValue, 4), "KeyValue - indent 4");
TEST_RESULT_STR(strPtr(json),
"{\n"
" \"backup-info-size-delta\" : 1982702,\n"
" \"backup-prior\" : \"20161219-212741F_20161219-212803I\",\n"
" \"backup-reference\" : [\n"
" \"20161219-212741F\",\n"
" \"20161219-212741F_20161219-212803I\"\n"
" ],\n"
" \"backup-timestamp-start\" : 1482182951,\n"
" \"checksum-page-error\" : [\n"
" 1\n"
" ],\n"
" \"section\" : {\n"
" \"key1\" : \"value1\",\n"
" \"key2\" : null,\n"
" \"key3\" : \"value2\"\n"
" }\n"
"}\n",
" sorted json string result");
//--------------------------------------------------------------------------------------------------------------------------
Variant *varListOuter = NULL;
TEST_ASSIGN(json, varToJson(varNewVarLst(varLstNew()), 0), "VariantList");
TEST_RESULT_STR(strPtr(json), "[]", " empty list no pretty print");
TEST_ASSIGN(varListOuter, varNewVarLst(varLstNew()), "new variant list with keyValues");
varLstAdd(varVarLst(varListOuter), keyValue);
TEST_ASSIGN(json, varToJson(varListOuter, 0), "VariantList - no indent");
TEST_RESULT_STR(strPtr(json),
"[{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"backup-timestamp-start\":1482182951,\"checksum-page-error\":[1],"
"\"section\":{\"key1\":\"value1\",\"key2\":null,\"key3\":\"value2\"}}]",
" sorted json string result no pretty print");
Variant *keyValue2 = varDup(keyValue);
varLstAdd(varVarLst(varListOuter), keyValue2);
TEST_ASSIGN(json, varToJson(varListOuter, 0), "VariantList - no indent - multiple elements");
TEST_RESULT_STR(strPtr(json),
"[{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"backup-timestamp-start\":1482182951,\"checksum-page-error\":[1],"
"\"section\":{\"key1\":\"value1\",\"key2\":null,\"key3\":\"value2\"}},"
"{\"backup-info-size-delta\":1982702,\"backup-prior\":\"20161219-212741F_20161219-212803I\","
"\"backup-reference\":[\"20161219-212741F\",\"20161219-212741F_20161219-212803I\"],"
"\"backup-timestamp-start\":1482182951,\"checksum-page-error\":[1],"
"\"section\":{\"key1\":\"value1\",\"key2\":null,\"key3\":\"value2\"}}]",
" sorted json string result no pretty print");
TEST_ASSIGN(json, varToJson(varListOuter, 2), "VariantList - indent 2 - multiple elements");
TEST_RESULT_STR(strPtr(json),
"[\n"
" {\n"
" \"backup-info-size-delta\" : 1982702,\n"
" \"backup-prior\" : \"20161219-212741F_20161219-212803I\",\n"
" \"backup-reference\" : [\n"
" \"20161219-212741F\",\n"
" \"20161219-212741F_20161219-212803I\"\n"
" ],\n"
" \"backup-timestamp-start\" : 1482182951,\n"
" \"checksum-page-error\" : [\n"
" 1\n"
" ],\n"
" \"section\" : {\n"
" \"key1\" : \"value1\",\n"
" \"key2\" : null,\n"
" \"key3\" : \"value2\"\n"
" }\n"
" },\n"
" {\n"
" \"backup-info-size-delta\" : 1982702,\n"
" \"backup-prior\" : \"20161219-212741F_20161219-212803I\",\n"
" \"backup-reference\" : [\n"
" \"20161219-212741F\",\n"
" \"20161219-212741F_20161219-212803I\"\n"
" ],\n"
" \"backup-timestamp-start\" : 1482182951,\n"
" \"checksum-page-error\" : [\n"
" 1\n"
" ],\n"
" \"section\" : {\n"
" \"key1\" : \"value1\",\n"
" \"key2\" : null,\n"
" \"key3\" : \"value2\"\n"
" }\n"
" }\n"
"]\n",
" sorted json string result, pretty print");
}
FUNCTION_HARNESS_RESULT_VOID();