libs/dfi/src/json_serializer.c (484 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "json_serializer.h" #include "dyn_type.h" #include "dyn_type_common.h" #include "celix_properties.h" #include "celix_array_list.h" #include "celix_array_list_encoding.h" #include "celix_err.h" #include "celix_stdlib_cleanup.h" #include <jansson.h> #include <assert.h> #include <stdint.h> #include <string.h> static int jsonSerializer_createType(const dyn_type* type, json_t* object, void** result); static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst); static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void* seqLoc); static int jsonSerializer_parseAny(const dyn_type* type, void* input, json_t* val); static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out); static int jsonSerializer_parseProperties(const dyn_type* type, json_t* object, void *inst); static int jsonSerializer_parseArrayList(const dyn_type* type, json_t* array, void *inst); static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** val); static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** val); static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out); static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t** out); static int jsonSerializer_writeProperties(const dyn_type* type, const void* input, json_t** out); static int jsonSerializer_writeArrayList(const dyn_type* type, const void* input, json_t** out); static int OK = 0; static int ERROR = 1; int jsonSerializer_deserialize(const dyn_type* type, const char* input, size_t length, void** result) { int status = 0; json_error_t error; json_auto_t* root = json_loadb(input, length, JSON_DECODE_ANY, &error); if (root == NULL) { celix_err_pushf("Error parsing json input '%.*s'. Error is: %s\n", (int)length, input, error.text); return ERROR; } status = jsonSerializer_deserializeJson(type, root, result); if (status != OK) { celix_err_pushf("Error cannot deserialize json. Input is '%s'", input); } return status; } int jsonSerializer_deserializeJson(const dyn_type* type, json_t* input, void** out) { return jsonSerializer_createType(dynType_realType(type), input, out); } static int jsonSerializer_createType(const dyn_type* type, json_t* val, void** result) { assert(val != NULL); int status = OK; void* inst = NULL; if ((status = dynType_alloc(type, &inst)) != OK) { return status; } if ((status = jsonSerializer_parseAny(type, inst, val)) != OK) { dynType_free(type, inst); *result = NULL; return status; } *result = inst; return OK; } static int jsonSerializer_parseObject(const dyn_type* type, json_t* object, void* inst) { assert(object != NULL); int status = OK; json_t* value; const struct complex_type_entries_head* entries = dynType_complex_entries(type); struct complex_type_entry* entry = NULL; int index = 0; void* valp = NULL; const dyn_type* valType = NULL; TAILQ_FOREACH(entry, entries, entries) { if (entry->name == NULL) { celix_err_push("Unamed field unsupported"); return ERROR; } value = json_object_get(object, entry->name); if (value == NULL) { celix_err_pushf("Missing object member %s", entry->name); return ERROR; } valp = dynType_complex_valLocAt(type, index, inst); valType = dynType_complex_dynTypeAt(type, index); status = jsonSerializer_parseAny(valType, valp, value); if (status != OK) { break; } index++; } return status; } static int jsonSerializer_parseAny(const dyn_type* type, void* loc, json_t* val) { int status = OK; const dyn_type* subType = NULL; char c = dynType_descriptorType(type); switch (c) { case 'Z' : *(bool*)loc = (bool) json_is_true(val); break; case 'F' : *(float*)loc = (float) json_real_value(val); break; case 'D' : *(double*)loc = json_real_value(val); break; case 'N' : *(int*)loc = (int) json_integer_value(val); break; case 'B' : *(char*)loc = (char) json_integer_value(val); break; case 'S' : *(int16_t*)loc = (int16_t) json_integer_value(val); break; case 'I' : *(int32_t*)loc = (int32_t) json_integer_value(val); break; case 'J' : *(int64_t*)loc = (int64_t) json_integer_value(val); break; case 'b' : *(uint8_t*)loc = (uint8_t) json_integer_value(val); break; case 's' : *(uint16_t*)loc = (uint16_t) json_integer_value(val); break; case 'i' : *(uint32_t*)loc = (uint32_t) json_integer_value(val); break; case 'j' : *(uint64_t*)loc = (uint64_t) json_integer_value(val); break; case 'E' : if (json_is_string(val)){ status = jsonSerializer_parseEnum(type, json_string_value(val), loc); } else { status = ERROR; celix_err_pushf("Expected json string for enum type but got %i", json_typeof(val)); } break; case 't' : if (json_is_null(val)) { // NULL string is allowed } else if (json_is_string(val)) { status = dynType_text_allocAndInit(type, loc, json_string_value(val)); } else { status = ERROR; celix_err_pushf("Expected json string type got %i", json_typeof(val)); } break; case 'p': if (json_is_null(val)) { // NULL celix_properties_t* is allowed } else if (json_is_object(val)) { status = jsonSerializer_parseProperties(type, val, loc); } else { status = ERROR; celix_err_pushf("Expected json object for celix_properties_t* type but got %i", json_typeof(val)); } break; case 'a': if (json_is_null(val)) { // NULL celix_array_list_t* is allowed } else if (json_is_array(val)) { status = jsonSerializer_parseArrayList(type, val, loc); } else { status = ERROR; celix_err_pushf("Expected json array for celix_array_list_t* type but got %i", json_typeof(val)); } break; case '[' : if (json_is_array(val)) { status = jsonSerializer_parseSequence(type, val, loc); } else { status = ERROR; celix_err_pushf("Expected json array type got '%i'", json_typeof(val)); } break; case '{' : if (json_is_object(val)) { status = jsonSerializer_parseObject(type, val, loc); } else { status = ERROR; celix_err_pushf("Expected json object type got '%i'", json_typeof(val)); } break; case '*' : subType = dynType_typedPointer_getTypedType(type); if (dynType_ffiType(subType) != &ffi_type_pointer) { // NULL pointer is allowed if (!json_is_null(val)) { status = jsonSerializer_createType(subType, val, (void **) loc); } } else { status = ERROR; celix_err_pushf("Error cannot deserialize pointer to pointer"); } break; //case 'P' : default : status = ERROR; celix_err_pushf("Error provided type '%c' not supported for JSON\n", c); break; } return status; } static int jsonSerializer_parseSequence(const dyn_type* seq, json_t* array, void* seqLoc) { assert(dynType_type(seq) == DYN_TYPE_SEQUENCE); int status = OK; size_t size = json_array_size(array); if (size > UINT32_MAX) { celix_err_pushf("Error array size(%zu) too large", size); return ERROR; } if ((status = dynType_sequence_alloc(seq, seqLoc, (uint32_t) size)) != OK) { return status; } const dyn_type* itemType = dynType_sequence_itemType(seq); size_t index; json_t* val; json_array_foreach(array, index, val) { void* valLoc = NULL; (void)dynType_sequence_increaseLengthAndReturnLastLoc(seq, seqLoc, &valLoc); status = jsonSerializer_parseAny(itemType, valLoc, val); if (status != OK) { break; } } return status; } static int jsonSerializer_parseProperties(const dyn_type* type, json_t* object, void *inst) { celix_autofree char* objStr = json_dumps(object, JSON_COMPACT | JSON_ENCODE_ANY); if (objStr == NULL) { celix_err_push("Error converting properties json object to string"); return ERROR; } int status = celix_properties_loadFromString(objStr, CELIX_PROPERTIES_DECODE_STRICT, (celix_properties_t**)inst); if (status != CELIX_SUCCESS) { celix_err_push("Error converting json object to properties"); return ERROR; } return OK; } static int jsonSerializer_parseArrayList(const dyn_type* type, json_t* array, void *inst) { celix_autofree char* arrayStr = json_dumps(array, JSON_COMPACT | JSON_ENCODE_ANY); if (arrayStr == NULL) { celix_err_push("Error converting array list json object to string"); return ERROR; } int status = celix_arrayList_loadFromString(arrayStr, CELIX_ARRAY_LIST_DECODE_STRICT, (celix_array_list_t**)inst); if (status != CELIX_SUCCESS) { celix_err_push("Error converting json object to array list"); return ERROR; } return OK; } int jsonSerializer_serialize(const dyn_type* type, const void* input, char** output) { int status; json_auto_t* root = NULL; if ((status = jsonSerializer_serializeJson(type, input, &root)) != OK) { return status; } *output = json_dumps(root, JSON_COMPACT | JSON_ENCODE_ANY); return *output != NULL ? OK : ERROR; } static int jsonSerializer_parseEnum(const dyn_type* type, const char* enum_name, int32_t* out) { struct meta_entry* entry; TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (0 == strcmp(enum_name, entry->name)) { *out = atoi(entry->value); return OK; } } celix_err_pushf("Could not find Enum value %s in enum type", enum_name); return ERROR; } int jsonSerializer_serializeJson(const dyn_type* type, const void* input, json_t** out) { return jsonSerializer_writeAny(dynType_realType(type), input, out); } static int jsonSerializer_writeAny(const dyn_type* type, const void* input, json_t** out) { int status = OK; int descriptor = dynType_descriptorType(type); json_auto_t* val = NULL; const dyn_type* subType = NULL; switch (descriptor) { case 'Z' : val = json_boolean(*(const bool*)input); break; case 'B' : val = json_integer((json_int_t)*(const char*)input); break; case 'S' : val = json_integer((json_int_t)*(const int16_t*)input); break; case 'I' : val = json_integer((json_int_t)*(const int32_t*)input); break; case 'J' : val = json_integer((json_int_t)*(const int64_t*)input); break; case 'b' : val = json_integer((json_int_t)*(const uint8_t*)input); break; case 's' : val = json_integer((json_int_t)*(const uint16_t*)input); break; case 'i' : val = json_integer((json_int_t)*(const uint32_t *)input); break; case 'j' : val = json_integer((json_int_t)*(const uint64_t*)input); break; case 'N' : val = json_integer((json_int_t)*(const int*)input); break; case 'F' : val = json_real((double) *(const float*)input); break; case 'D' : val = json_real(*(const double*)input); break; case 't' : { const char *strValue = *(const char **) input; val = (strValue != NULL) ? json_string(strValue) : json_null(); break; } case 'E': status = jsonSerializer_writeEnum(type, *(const int32_t*)input, &val); break; case 'p' : status = jsonSerializer_writeProperties(type, input, &val); break; case 'a' : status = jsonSerializer_writeArrayList(type, input, &val); break; case '*' : subType = dynType_typedPointer_getTypedType(type); if (dynType_ffiType(subType) != &ffi_type_pointer) { const void* inputValue = *(const void**)input; if (inputValue) { status = jsonSerializer_writeAny(subType, inputValue, &val); } else { val = json_null(); } } else { status = ERROR; celix_err_pushf("Error cannot serialize pointer to pointer"); } break; case '{' : status = jsonSerializer_writeComplex(type, input, &val); break; case '[' : status = jsonSerializer_writeSequence(type, input, &val); break; default : celix_err_pushf("Unsupported descriptor '%c'", descriptor); status = ERROR; break; } if (status != OK) { return status; } *out = celix_steal_ptr(val); return *out != NULL ? OK : ERROR; } static int jsonSerializer_writeSequence(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_SEQUENCE); json_auto_t* array = json_array(); if (array == NULL) { return ERROR; } const dyn_type* itemType = dynType_sequence_itemType(type); uint32_t len = dynType_sequence_length(input); for (uint32_t i = 0; i < len; i += 1) { int status = OK; void* itemLoc = NULL; json_t* item = NULL; if ((status = dynType_sequence_locForIndex(type, input, i, &itemLoc)) != OK) { celix_err_push("Cannot serialize invalid sequence"); return status; } if ((status = jsonSerializer_writeAny(itemType, itemLoc, &item)) != OK) { return status; } if ((json_array_append_new(array, item)) != 0) { return ERROR; } } *out = celix_steal_ptr(array); return OK; } static int jsonSerializer_writeComplex(const dyn_type* type, const void* input, json_t** out) { assert(dynType_type(type) == DYN_TYPE_COMPLEX); json_auto_t* val = json_object(); if (val == NULL) { return ERROR; } struct complex_type_entry* entry = NULL; const struct complex_type_entries_head* entries = dynType_complex_entries(type); int index = 0; TAILQ_FOREACH(entry, entries, entries) { int status; void* subLoc = NULL; json_t* subVal = NULL; const dyn_type* subType = NULL; if (entry->name == NULL) { celix_err_push("Unamed field unsupported"); return ERROR; } subLoc = dynType_complex_valLocAt(type, index, (void*)input); subType = dynType_complex_dynTypeAt(type, index); if ((status = jsonSerializer_writeAny(subType, subLoc, &subVal)) != OK) { return status; } if (json_object_set_new(val, entry->name, subVal) != 0) { return ERROR; } index++; } *out = celix_steal_ptr(val); return OK; } static int jsonSerializer_writeEnum(const dyn_type* type, int32_t enum_value, json_t **out) { struct meta_entry * entry; // Convert to string char enum_value_str[32]; snprintf(enum_value_str, 32, "%d", enum_value); // Lookup in meta-information TAILQ_FOREACH(entry, &type->metaProperties, entries) { if (0 == strcmp(enum_value_str, entry->value)) { *out = json_string((const char*)entry->name); return *out != NULL ? OK : ERROR; } } celix_err_pushf("Could not find Enum value %s in enum type", enum_value_str); return ERROR; } static int jsonSerializer_writeProperties(const dyn_type* type, const void* input, json_t** out) { celix_properties_t* props = *(celix_properties_t**)input; if (props == NULL) { *out = json_null(); if (*out == NULL) { celix_err_push("Failed to create json object for null properties."); return ERROR; } return OK; } celix_autofree char* str = NULL; celix_status_t status = celix_properties_saveToString(props, CELIX_PROPERTIES_ENCODE_STRICT, &str); if (status != CELIX_SUCCESS) { celix_err_push("Failed to convert properties to string."); return ERROR; } json_error_t jsonError = {0}; *out = json_loads(str, 0, &jsonError); if (*out == NULL) { celix_err_pushf("Failed to convert properties string to json. Error: %s:%i:%i: %s.", jsonError.source, jsonError.line, jsonError.column, jsonError.text); return ERROR; } return OK; } static int jsonSerializer_writeArrayList(const dyn_type* type, const void* input, json_t** out) { celix_array_list_t* list = *(celix_array_list_t**)input; if (list == NULL) { *out = json_null(); if (*out == NULL) { celix_err_push("Failed to create json object for null array list."); return ERROR; } return OK; } celix_autofree char* str = NULL; celix_status_t status = celix_arrayList_saveToString(list, CELIX_ARRAY_LIST_ENCODE_STRICT, &str); if (status != CELIX_SUCCESS) { celix_err_push("Failed to convert array list to string."); return ERROR; } json_error_t jsonError = {0}; *out = json_loads(str, 0, &jsonError); if (*out == NULL) { celix_err_pushf("Failed to convert array list string to json. Error: %s:%i:%i: %s.", jsonError.source, jsonError.line, jsonError.column, jsonError.text); return ERROR; } return OK; }