You've already forked pgbackrest
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:
committed by
David Steele
parent
e641c130d3
commit
f4a1751abc
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -190,7 +190,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: type-json
|
||||
total: 1
|
||||
total: 3
|
||||
|
||||
coverage:
|
||||
common/type/json: full
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user