Tue, 06 Jan 2026 20:59:06 +0100
add dbuJsonToList tests
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2025 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include "json.h" /* ---------------- Obj to JSON serialization functions ---------------------*/ CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a) { return dbuObjectToJson2(type, obj, a, false, false); } CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString) { if(obj == NULL) { return cxJsonCreateLiteral(a, CX_JSON_NULL); } if(!a) { a = cxDefaultAllocator; } CxJsonValue *value = cxJsonCreateObj(a); if(!value) { return NULL; } // add all primitive values CxMapIterator i = cxMapIteratorValues(type->fields); for(int n=0;n<2;n++) { cx_foreach(DBUField *, field, i) { CxJsonValue *child = NULL; if(field->toBool) { bool b = field->toBool(field, obj); child = cxJsonCreateLiteral(a, b ? CX_JSON_TRUE : CX_JSON_FALSE); } else if(field->toInt64) { int64_t i = field->toInt64(field, obj); child = cxJsonCreateInteger(a, i); } else if(field->toUInt64) { uint64_t u = field->toUInt64(field, obj); child = cxJsonCreateInteger(a, (int64_t)u); } else if(field->toDouble) { double d = field->toDouble(field, obj); child = cxJsonCreateNumber(a, d); } else if(field->toString) { cxmutstr s = field->toString(field, obj, a); if(s.ptr) { // we don't want to copy the string again, therefore // we can't use cxJsonCreateString child = cxMalloc(a, sizeof(CxJsonValue)); if(!child) { cxFree(a, s.ptr); return NULL; } child->allocator = a; child->type = CX_JSON_STRING; child->string = s; } } else if(field->toBinary) { // TODO } else if(field->toObject) { DBUObject child_obj = field->toObject(field, obj); if(child_obj == NULL) { child = CX_JSON_NULL; } else if(field->objType) { child = dbuObjectToJson2(field->objType, child_obj, a, setNull, forceString); } } else if(field->toList) { DBUAbstractList *list = field->toList(field, obj); child = cxJsonCreateArr(a, list->length(list)); if(child) { if(list->iterator) { CxIterator iter = list->iterator(list); cx_foreach(void *, elm, iter) { DBUField *f = list->elementField(list, elm); if(f->toObject) { DBUObject child_obj = f->toObject(field, elm); if(f->objType) { CxJsonValue *array_elm = dbuObjectToJson2(f->objType, child_obj, a, setNull, forceString); cxJsonArrAddValues(child, &array_elm, 1); } } } } } } else { continue; // non-serializable field } if(!child) { cxJsonValueFree(value); return NULL; } if(cxJsonObjPut(value, field->name, child)) { cxJsonValueFree(child); cxJsonValueFree(value); return NULL; } } i = cxMapIteratorValues(type->obj_fields); } return value; } /* --------------- Json to Obj deserialization functions --------------------*/ static void* jsonToObj(DBUClass *type, const CxAllocator *a, CxJsonValue *value, int depth, int *error) { if(!cxJsonIsObject(value)) { *error = 1; return NULL; } if(depth > DBU_JSON_MAX_DEPTH) { *error = 2; return NULL; } if(!a) { a = cxDefaultAllocator; } void *obj = cxMalloc(a, type->obj_size); if(obj) { memset(obj, 0, type->obj_size); } else { *error = 3; return NULL; } char buf[64]; int len = 0; CxMapIterator i = cxMapIterator(value->object); cx_foreach(CxMapEntry *, entry, i) { DBUField *field = cxMapGet(type->fields, entry->key); if(!field) { field = cxMapGet(type->obj_fields, entry->key); if(!field) { continue; } } len = 0; CxJsonValue *child = entry->value; switch(child->type) { case CX_JSON_LITERAL: { if(child->literal == CX_JSON_NULL) { if(field->initObjValue) { field->initObjValue(field, a, obj, NULL); } break; } else { int b = child->literal == CX_JSON_TRUE; if(field->initIntValue) { field->initIntValue(field, a, obj, b); continue; } else { len = snprintf(buf, 64, "%d", b); } } break; } case CX_JSON_INTEGER: { if(field->initIntValue) { field->initIntValue(field, a, obj, child->integer); } else { len = snprintf(buf, 64, "%" PRId64, child->integer); } break; } case CX_JSON_NUMBER: { if(field->initDoubleValue) { field->initDoubleValue(field, a, obj, child->number); } else { len = snprintf(buf, 64, "%f", child->number); } break; } case CX_JSON_STRING: { cxmutstr str = child->string; if(field->initValue(field, a, obj, str.ptr, str.length)) { *error = 3; free(obj); // TODO: improve obj cleanup return NULL; } break; } case CX_JSON_OBJECT: { if(field->objType && field->initObjValue) { void *child_obj = jsonToObj(field->objType, a, child, depth+1, error); if(child_obj) { field->initObjValue(field, a, obj, child_obj); } else { free(obj); // TODO: improve obj cleanup return NULL; } } break; } case CX_JSON_ARRAY: { if(field->builder.add) { for(int i=0;i<child->array.size;i++) { CxJsonValue *elm = child->array.data[i]; void *elm_obj = NULL; bool add_elm = false; if(cxJsonIsObject(elm)) { elm_obj = jsonToObj(field->builder.type, a, elm, depth+1, error); if(!elm_obj) { free(obj); // TODO: improve obj cleanup return NULL; } add_elm = true; } else if(cxJsonIsLiteral(elm) && elm->literal == CX_JSON_NULL) { add_elm = true; } if(add_elm) { if(field->builder.add(&field->builder, obj, field->builder.type, elm_obj, NULL, a)) { free(obj); // TODO: improve obj cleanup free(elm_obj); *error = 3; return NULL; } } } } break; } default: break; } if(len > 0) { if(field->initValue(field, a, obj, buf, len)) { // TODO: completely cleanup obj free(obj); *error = 3; return NULL; } } } *error = 0; return obj; } void* dbuJsonToObject(DBUClass *type, const CxAllocator *a, CxJsonValue *value) { int error; return jsonToObj(type, a, value, 0, &error); } static CxList* jsonToArray(DBUClass *type, const CxAllocator *a, CxJsonValue *value, size_t elmSize, int *error) { if(!cxJsonIsArray(value)) { *error = 1; return NULL; } CxList *list = cxArrayListCreate(a, elmSize, value->array.size); if(!list) { *error = 3; } for(size_t i=0;i<value->array.size;i++) { CxJsonValue *elm = value->array.data[i]; void *elm_obj = NULL; if(cxJsonIsObject(elm)) { elm_obj = jsonToObj(type, a, elm, 1, error); if(!elm_obj) { cxListFree(list); return NULL; } } if(cxListAdd(list, elm_obj)) { *error = 3; cxListFree(list); return NULL; } } return list; } CxList* dbuJsonToList(DBUClass *type, const CxAllocator *a, CxJsonValue *value) { int error; return jsonToArray(type, a, value, CX_STORE_POINTERS, &error); }