Added simple load & save fns for cJSON objects, with unit tests

This commit is contained in:
ares 2020-11-21 19:44:26 -05:00
parent 5437b79086
commit 0b7c916252
4 changed files with 362 additions and 0 deletions

145
cJSON.c
View File

@ -2988,3 +2988,148 @@ CJSON_PUBLIC(void) cJSON_free(void *object)
{
global_hooks.deallocate(object);
}
/* This will write out a json object to the specified file name
Args:
filename - the name of the file to load
**item - a ptr to the cJSON structure to store the parsed root item in
**errorMessage - an optional field to return an error message
Returns:
1 - on success
0 - The provided object was NULL
-1 - on failure to open the file to write
-2 - on failure to stringify the cJSON object
*/
CJSON_PUBLIC(int) cJSON_saveJSONfile(const char *filename, cJSON *item, char *errorMessage) {
FILE *fptr = NULL;
char *json_str = NULL;
/* Null check the incoming object */
if (item == NULL) { return 0; }
/* Open the target file for writting */
fptr = fopen(filename, "w");
if (fptr == NULL) {
if (errorMessage != NULL)
{
sprintf(errorMessage, "Cannot open the file '%s' to write.", filename);
}
return -1;
}
/* Print out the json string to the file */
json_str = cJSON_Print(item);
if (json_str != NULL)
{
fprintf(fptr, "%s", json_str);
fprintf(fptr, "\n");
free(json_str);
}
else
{
if (errorMessage != NULL)
{
sprintf(errorMessage, "Failed to print json object to a string.");
}
fclose(fptr);
return -2;
}
fclose(fptr);
/* Success */
return 1;
}
/* This will load and parse a json file.
Args:
filename - the name of the file to load
**item - a ptr to the cJSON structure to store the parsed root item in
**errorMessage - an optional field to return an error message
Returns:
1 - on success
0 - file not found
-1 - on intial memory allocation failure
-2 - on failure to reallocate more memory
-3 - bad filename arg
-4 - bad item arg
*/
#define cJSON_LOAD_BUFFER_INCREMENT 2048
CJSON_PUBLIC(int) cJSON_loadJSONfile(const char *filename, cJSON **item, char *errorMessage) {
FILE *fptr;
char *buffer;
size_t bufferSize = cJSON_LOAD_BUFFER_INCREMENT;
size_t bytesRead = 0;
int i = 0;
/* Sanity check the item arg*/
if (item == NULL) {
return -4;
}
/* Sanity check the filename arg */
if (filename == NULL) {
if (errorMessage != NULL)
{
sprintf(errorMessage, "The filename was NULL");
}
return -3;
}
/* Open the file */
fptr = fopen(filename, "r");
if (fptr == NULL) {
if (errorMessage != NULL)
{
sprintf(errorMessage, "Cannot open the file '%s' to read.", filename);
}
return 0;
}
/* Allocate the buffer to it's initial size */
buffer = (char *)malloc(bufferSize + cJSON_LOAD_BUFFER_INCREMENT);
if (buffer == NULL) {
if (errorMessage != NULL)
{
sprintf(errorMessage, "Memory allocation error while reading '%s'.", filename);
}
return -1;
}
/* Read in the file until there's nothing left to read
Keep reading in the file, expanding our buffer if needed */
while ((bytesRead = fread(buffer + (cJSON_LOAD_BUFFER_INCREMENT * i), 1, cJSON_LOAD_BUFFER_INCREMENT, fptr)) > 0) {
if (bytesRead == sizeof(buffer))
{
/* We filled the buffer, realloc the final buffer so it's larger */
buffer = (char *)realloc(buffer, bufferSize + cJSON_LOAD_BUFFER_INCREMENT);
if (buffer == NULL)
{
if (errorMessage != NULL)
{
sprintf(errorMessage, "Memory allocation error while reading '%s'.", filename);
}
return -2;
}
/* Keep track of where we are in our enlarged buffer */
i++;
}
}
/* We're done with the file */
fclose(fptr);
/* Clear any existing structure in memory */
if (*item != NULL)
{
cJSON_Delete(*item);
}
/* Parse the final buffer */
*item = cJSON_Parse(buffer);
return 1;
}

33
cJSON.h
View File

@ -273,6 +273,39 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
/* This will write out a json object to the specified file name
Args:
filename - the name of the file to load
**item - a ptr to the cJSON structure to store the parsed root item in
**errorMessage - an optional field to return an error message
Returns:
1 - on success
-1 - on failure to open the file to write
-2 - on failure to stringify the cJSON object
*/
CJSON_PUBLIC(int) cJSON_saveJSONfile(const char *filename, cJSON *item, char *errorMessage);
/* This will load and parse a json file.
Args:
filename - the name of the file to load
**item - a ptr to the cJSON structure to store the parsed root item in.
If an object already exists at this ptr value, it will be deleted
and overwritten.
**errorMessage - an optional field to return an error message
Returns:
1 - on success
0 - file not found
-1 - on failure to open the file to read
-2 - on intial memory allocation failure
-3 - on failure to reallocate more memory
-4 - bad filename arg
*/
CJSON_PUBLIC(int) cJSON_loadJSONfile(const char *filename, cJSON **item, char *errorMessage);
#ifdef __cplusplus
}
#endif

View File

@ -57,6 +57,7 @@ if(ENABLE_CJSON_TEST)
compare_tests
cjson_add
readme_examples
file_functions
)
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")

183
tests/file_functions.c Normal file
View File

@ -0,0 +1,183 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* These are unit tests for the cJSON_saveJSONfile & cJSON_loadJSONfile functions */
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
#define TEST_VALUE "test value"
static void save_file_should_succeed(void)
{
char *errorMessage = NULL;
cJSON *object;
int rc = 0;
errorMessage = (char *)malloc(512);
object = cJSON_CreateObject();
cJSON_AddNumberToObject(object, TEST_VALUE, 99);
rc = cJSON_saveJSONfile("./testfile", object, errorMessage);
TEST_ASSERT_EQUAL(1, rc);
free(errorMessage);
}
static void save_file_with_bad_path(void)
{
char *errorMessage = NULL;
cJSON *object;
int rc = 0;
errorMessage = (char *)malloc(512);
object = cJSON_CreateObject();
rc = cJSON_saveJSONfile("/!@#$!this doesn't exist/testfile", object, errorMessage);
TEST_ASSERT_EQUAL(-1, rc);
TEST_ASSERT_EQUAL_STRING("Cannot open the file '/!@#$!this doesn't exist/testfile' to write.", errorMessage);
free(errorMessage);
}
static void save_file_with_bad_path_no_error_msg(void)
{
char *errorMessage = NULL;
cJSON *object;
int rc = 0;
object = cJSON_CreateObject();
rc = cJSON_saveJSONfile("/!@#$!this doesn't exist/testfile", object, errorMessage);
TEST_ASSERT_EQUAL(-1, rc);
TEST_ASSERT_EQUAL_STRING(NULL, errorMessage);
free(errorMessage);
cJSON_Delete(object);
}
static void save_file_with_NULL_object(void)
{
char *errorMessage = NULL;
int rc = 0;
errorMessage = (char *)malloc(512);
rc = cJSON_saveJSONfile("./testfile", NULL, errorMessage);
TEST_ASSERT_EQUAL(0, rc);
free(errorMessage);
}
static void load_file_successs_preexisting_object(void)
{
char *errorMessage = NULL;
cJSON *object = NULL;
int rc = 0;
double testValue = 0;
cJSON *numberJson = NULL;
object = cJSON_CreateObject();
errorMessage = (char *)malloc(512);
rc = cJSON_loadJSONfile("./testfile", &object, errorMessage);
numberJson = cJSON_GetObjectItem(object, TEST_VALUE);
testValue = cJSON_GetNumberValue(numberJson);
TEST_ASSERT_EQUAL(1, rc);
TEST_ASSERT_EQUAL(99, testValue);
free(errorMessage);
cJSON_Delete(object);
}
static void load_file_success(void)
{
char *errorMessage = NULL;
cJSON *object = NULL;
int rc = 0;
double testValue = 0;
cJSON *numberJson = NULL;
errorMessage = (char *)malloc(512);
rc = cJSON_loadJSONfile("./testfile", &object, errorMessage);
numberJson = cJSON_GetObjectItem(object, TEST_VALUE);
testValue = cJSON_GetNumberValue(numberJson);
TEST_ASSERT_EQUAL(1, rc);
TEST_ASSERT_EQUAL(99, testValue);
free(errorMessage);
}
static void load_file_bad_filename(void)
{
char *errorMessage = NULL;
cJSON *object = NULL;
int rc = 0;
errorMessage = (char *)malloc(512);
rc = cJSON_loadJSONfile("./notfound", &object, errorMessage);
TEST_ASSERT_EQUAL_STRING("Cannot open the file './notfound' to read.",errorMessage);
TEST_ASSERT_EQUAL(0, rc);
free(errorMessage);
}
static void load_file_NULL_filename(void)
{
char *errorMessage = NULL;
cJSON *object = NULL;
int rc = 0;
errorMessage = (char *)malloc(512);
rc = cJSON_loadJSONfile(NULL, &object, errorMessage);
TEST_ASSERT_EQUAL(-3, rc);
TEST_ASSERT_EQUAL_STRING("The filename was NULL",errorMessage);
free(errorMessage);
}
static void load_file_NULL_object(void)
{
char *errorMessage = NULL;
int rc = 0;
errorMessage = (char *)malloc(512);
rc = cJSON_loadJSONfile("./testfile", NULL, errorMessage);
TEST_ASSERT_EQUAL(-4, rc);
free(errorMessage);
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(save_file_with_bad_path);
RUN_TEST(save_file_with_bad_path_no_error_msg);
RUN_TEST(save_file_with_NULL_object);
RUN_TEST(save_file_should_succeed);
RUN_TEST(load_file_success);
RUN_TEST(load_file_successs_preexisting_object);
RUN_TEST(load_file_bad_filename);
RUN_TEST(load_file_NULL_filename);
RUN_TEST(load_file_NULL_object);
return UNITY_END();
}