Files
obitools3/src/libjson/json_utils.c

283 lines
7.7 KiB
C
Raw Normal View History

/****************************************************************************
* JSON utils functions *
****************************************************************************/
/**
* @file json_utils.c
* @author Celine Mercier (celine.mercier@metabarcoding.org)
* @date August 23th 2018
* @brief Functions formatting and handling json-formatted comments for DMS', views and columns.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "../obierrno.h"
#include "../obidebug.h"
#include "./json_utils.h"
#include "./cJSON.h"
#define DEBUG_LEVEL 0 // TODO has to be defined somewhere else (cython compil flag?)
/**************************************************************************
*
* D E C L A R A T I O N O F T H E P R I V A T E F U N C T I O N S
*
**************************************************************************/
/**
* Internal function reading a character string as a cJSON structure.
*
* @warning The returned pointer has to be freed by the caller.
*
* @param comments The character string to read.
*
* @returns A pointer on a cJSON structure.
* @retval NULL if an error occurred.
*
* @since August 2018
* @author Celine Mercier (celine.mercier@metabarcoding.org)
*/
cJSON* read_comments(const char* comments);
/**
* Internal function adding a key/value pair to a cJSON structure.
*
* @warning If the key is already in the structure, its associated value is turned
* into an array if needed and the new value is added to that array.
* // TODO discuss replace boolean
*
* @param comments_struct The cJSON structure to which the pair should be added.
* @param key The key of the key/value pair to add.
* @param value The value of the key/value pair to add.
*
* @returns A pointer on the cJSON structure with the added element.
* @retval NULL if an error occurred.
*
* @since August 2018
* @author Celine Mercier (celine.mercier@metabarcoding.org)
*/
cJSON* add_comment_to_struct(cJSON* comments_struct, const char* key, const char* value);
/************************************************************************
*
* D E F I N I T I O N O F T H E P R I V A T E F U N C T I O N S
*
************************************************************************/
cJSON* read_comments(const char* comments)
{
cJSON* comments_struct = cJSON_Parse(comments);
if (comments_struct == NULL)
{
const char* error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
fprintf(stderr, "JSON error before: %s\n", error_ptr);
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError reading comments as a cJSON structure");
return NULL;
}
return comments_struct;
}
cJSON* add_comment_to_struct(cJSON* comments_struct, const char* key, const char* value)
{
cJSON* array = NULL;
cJSON* element = NULL;
cJSON* array_element = NULL;
cJSON* value_struct = NULL;
cJSON* old_string = NULL;
char* elt_value=NULL;
cJSON_ArrayForEach(element, comments_struct)
{
if (strcmp(key, element->string) == 0)
{
// Check if value is already in
// If string, compare value
if (cJSON_IsString(element))
{
elt_value = cJSON_Print(element);
if (strcmp(value, elt_value) == 0)
{// If same value, done
free(elt_value);
return comments_struct;
}
free(elt_value);
}
else
{
if (cJSON_IsArray(element))
// Go through values to check if new value is already in
{
cJSON_ArrayForEach(array_element, element)
{
// Compare value
elt_value = cJSON_Print(array_element);
if (strcmp(value, elt_value) == 0)
{// If same value, done
free(elt_value);
return comments_struct;
}
free(elt_value);
}
}
}
// If value was not found, turn the element into an array (if not array already) and append
if (cJSON_IsString(element))
{ // Turn the string item into an array
array = cJSON_CreateArray();
if (array == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError creating an array in a cJSON structure");
return NULL;
}
cJSON_AddItemToObject(comments_struct, key, array);
// Detach the existing string and insert it in the array
old_string = cJSON_DetachItemFromObjectCaseSensitive(comments_struct, key);
cJSON_AddItemToArray(array, old_string);
}
else if (cJSON_IsArray(element))
{
// Add value in array
array = element;
}
else
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError adding a key/value pair to a cJSON structure: a new value can be added to an existing key"
"only if the existing value is either a character string or an array");
return NULL;
}
// Convert value to cJSON structure
value_struct = cJSON_CreateString(value);
if (value_struct == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError creating a cJSON character string to add a new value to a cJSON structure");
return NULL;
}
// Add new value item to the array
cJSON_AddItemToArray(array, value_struct);
// Done
return comments_struct;
}
}
// If key not already in, add with value
value_struct = cJSON_CreateString(value);
if (value_struct == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError creating a cJSON character string to add a new value to a cJSON structure");
return NULL;
}
cJSON_AddItemToObject(comments_struct, key, value_struct);
return comments_struct;
}
/**********************************************************************
*
* D E F I N I T I O N O F T H E P U B L I C F U N C T I O N S
*
**********************************************************************/
char* obi_add_comment(char* comments, const char* key, const char* value)
{
cJSON* comments_struct=NULL;
char* new_comments=NULL;
// If NULL or empty comments, error
if ((comments == NULL) || (strcmp(comments, "") == 0))
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError adding a key/value pair to a comments character string: comments is NULL");
return NULL;
}
comments_struct = cJSON_Parse(comments);
if (comments_struct == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError parsing the comments when adding a comment to a view, key: %s, value: %s", key, value);
return NULL;
}
comments_struct = add_comment_to_struct(comments_struct, key, value);
if (comments_struct == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError adding a comment to a view, key: %s, value: %s", key, value);
return NULL;
}
// Print to char*
new_comments = cJSON_Print(comments_struct);
if (new_comments == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError building the new comments string when adding a comment to a view, key: %s, value: %s", key, value);
return NULL;
}
// Free structure
cJSON_Delete(comments_struct);
return new_comments;
}
char* obi_read_comment(char* comments, const char* key)
{
cJSON* comments_struct = NULL;
cJSON* value_json = NULL;
char* value = NULL;
comments_struct = cJSON_Parse(comments);
if (comments_struct == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError parsing the comments when reading comments, key: %s", key);
return NULL;
}
value_json = cJSON_GetObjectItem(comments_struct, key);
if (value_json == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError getting a value when reading a comment, key: %s", key);
return NULL;
}
value = cJSON_Print(value_json);
if (value == NULL)
{
obi_set_errno(OBI_JSON_ERROR);
obidebug(1, "\nError formatting a value when reading a comment, key: %s", key);
return NULL;
}
// Free structure
cJSON_Delete(comments_struct);
return value;
}