Thu, 11 Dec 2025 22:58:02 +0100
implement list to json serialization
| dbutils/dbutils/dbutils.h | file | annotate | diff | comparison | revisions | |
| dbutils/field.c | file | annotate | diff | comparison | revisions | |
| dbutils/field.h | file | annotate | diff | comparison | revisions | |
| dbutils/json.c | file | annotate | diff | comparison | revisions | |
| test/json.c | file | annotate | diff | comparison | revisions | |
| test/json.h | file | annotate | diff | comparison | revisions | |
| test/main.c | file | annotate | diff | comparison | revisions |
--- a/dbutils/dbutils/dbutils.h Wed Dec 10 18:36:02 2025 +0100 +++ b/dbutils/dbutils/dbutils.h Thu Dec 11 22:58:02 2025 +0100 @@ -280,7 +280,7 @@ * * This should only be implemented, if the field is a list. */ - DBUAbstractList (*toList)(DBUField *f); + DBUAbstractList* (*toList)(DBUField *f, DBUObject obj); /* * destructor (optional)
--- a/dbutils/field.c Wed Dec 10 18:36:02 2025 +0100 +++ b/dbutils/field.c Thu Dec 11 22:58:02 2025 +0100 @@ -1250,6 +1250,45 @@ return 0; } +static size_t dbu_cxlist_length(DBUAbstractList *list) { + DBUListImpl *ls = (DBUListImpl*)list; + return cxListSize(ls->list.list); +} + +static void* dbu_cxlist_get(DBUAbstractList *list, size_t index) { + DBUListImpl *ls = (DBUListImpl*)list; + return cxListAt(ls->list.list, index); +} + +static DBUField* listimpl_field(DBUAbstractList *list, void *elm) { + DBUListImpl *ls = (DBUListImpl*)list; + return &ls->field; +} + +static CxIterator dbu_cxlist_iterator(DBUAbstractList *list) { + DBUListImpl *ls = (DBUListImpl*)list; + return cxListIterator(ls->list.list); +} + +static DBUObject elmfield_toObject(DBUField *f, DBUObject obj) { + return obj; +} + +static DBUAbstractList* cxlist_tolist(DBUField *f, DBUObject obj) { + DBUOffsetField *field = (DBUOffsetField*)f; + DBUListImpl *list = malloc(sizeof(DBUListImpl)); + list->list.list = *(CxList**)(obj+field->offset);; + list->list.length = dbu_cxlist_length; + list->list.get = dbu_cxlist_get; + list->list.iterator = dbu_cxlist_iterator; + list->list.elementField = listimpl_field; + list->list.free = (void(*)(DBUAbstractList*))free; + memset(&list->field, 0, sizeof(DBUField)); + list->field.toObject = elmfield_toObject; + list->field.objType = field->field.builder.userdata2; + return (DBUAbstractList*)list; +} + void dbuClassAddCxLinkedList(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls) { DBUOffsetField *field = malloc(sizeof(DBUOffsetField)); memset(field, 0, sizeof(DBUOffsetField)); @@ -1259,7 +1298,10 @@ field->field.builder.userdata2 = foreign_cls; field->field.builder.create = linkedlist_create_obj; field->field.builder.add = linkedlist_add; + field->field.toList = cxlist_tolist; + // TODO: can we pass name to dbuClassAddObjField? dbuClassAddObjField(cls, NULL, (DBUField*)field, foreign_cls); + field->field.name = name ? cx_strdup(cx_str(name)) : (cxmutstr){NULL,0}; //dbuClassAddParentObjField(foreign_cls, name, cls, (DBUField*)field); }
--- a/dbutils/field.h Wed Dec 10 18:36:02 2025 +0100 +++ b/dbutils/field.h Thu Dec 11 22:58:02 2025 +0100 @@ -61,6 +61,16 @@ } DBUObjLenField; + +/* + * can be used by all DBUAbstractList implementations + */ +typedef struct DBUListImpl { + DBUAbstractList list; + DBUField field; +} DBUListImpl; + + DBUField* dbuFieldCreateInt32(off_t offset); DBUField* dbuFieldCreateUInt32(off_t offset);
--- a/dbutils/json.c Wed Dec 10 18:36:02 2025 +0100 +++ b/dbutils/json.c Thu Dec 11 22:58:02 2025 +0100 @@ -86,7 +86,23 @@ child = dbuObjectToJson2(field->objType, child_obj, a, setNull, forceString); } } else if(field->toList) { - // TODO + DBUAbstractList *list = field->toList(field, obj); + child = cxJsonCreateArr(a); + 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 }
--- a/test/json.c Wed Dec 10 18:36:02 2025 +0100 +++ b/test/json.c Thu Dec 11 22:58:02 2025 +0100 @@ -30,7 +30,7 @@ #include <stdbool.h> #include <cx/buffer.h> - +#include <cx/linked_list.h> typedef struct Test1 { char *str; @@ -64,11 +64,17 @@ Test2 *test2; } Test4; +typedef struct Test5 { + char *test; + CxList *test2List; +} Test5; + static DBUContext *ctx; static DBUClass *test1_class; static DBUClass *test2_class; static DBUClass *test3_class; static DBUClass *test4_class; +static DBUClass *test5_class; int init_json_tests(void) { ctx = dbuContextCreate(); @@ -100,6 +106,10 @@ dbuClassAdd(test4_class, Test4, id); dbuClassAddObj(test4_class, "test2", offsetof(Test4, test2), test2_class); dbuClassAddObj(test4_class, "test3", offsetof(Test4, test3), test3_class); + + test5_class = dbuRegisterClassWithoutPK(ctx, "test5", sizeof(Test5)); + dbuClassAdd(test5_class, Test5, test); + dbuClassAddCxLinkedList(test5_class, "test2List", offsetof(Test5, test2List), test2_class); } void cleanup_json_tests(void) { @@ -239,3 +249,69 @@ cxJsonValueFree(value); } } + +CX_TEST(testObjectToJsonChildList) { + Test5 test5; + test5.test = "hello"; + test5.test2List = cxLinkedListCreateSimple(CX_STORE_POINTERS); + + Test2 c1; + c1.i = 1; + c1.name = "c1"; + + Test2 c2; + c2.i = 2; + c2.name = "c2"; + + Test2 c3; + c3.i = 3; + c3.name = "c3"; + + cxListAdd(test5.test2List, &c1); + cxListAdd(test5.test2List, &c2); + cxListAdd(test5.test2List, &c3); + + CX_TEST_DO { + CxJsonValue *value = dbuObjectToJson(test5_class, &test5, NULL); + CX_TEST_ASSERT(value); + CX_TEST_ASSERT(cxJsonIsObject(value)); + + CxJsonValue *v = cxJsonObjGet(value, "test"); + CX_TEST_ASSERT(v); + CX_TEST_ASSERT(cxJsonIsString(v)); + CX_TEST_ASSERT(!cx_strcmp(v->value.string, test5.test)); + + v = cxJsonObjGet(value, "test2List"); + CX_TEST_ASSERT(v); + CX_TEST_ASSERT(cxJsonIsArray(v)); + + CxJsonValue *x = cxJsonArrGet(v, 0); + CX_TEST_ASSERT(cxJsonIsObject(x)); + CxJsonValue *z = cxJsonObjGet(x, "i"); + CX_TEST_ASSERT(cxJsonIsInteger(z)); + CX_TEST_ASSERT(z->value.integer == c1.i); + z = cxJsonObjGet(x, "name"); + CX_TEST_ASSERT(cxJsonIsString(z)); + CX_TEST_ASSERT(!cx_strcmp(z->value.string, c1.name)); + + x = cxJsonArrGet(v, 1); + CX_TEST_ASSERT(cxJsonIsObject(x)); + z = cxJsonObjGet(x, "i"); + CX_TEST_ASSERT(cxJsonIsInteger(z)); + CX_TEST_ASSERT(z->value.integer == c2.i); + z = cxJsonObjGet(x, "name"); + CX_TEST_ASSERT(cxJsonIsString(z)); + CX_TEST_ASSERT(!cx_strcmp(z->value.string, c2.name)); + + x = cxJsonArrGet(v, 2); + CX_TEST_ASSERT(cxJsonIsObject(x)); + z = cxJsonObjGet(x, "i"); + CX_TEST_ASSERT(cxJsonIsInteger(z)); + CX_TEST_ASSERT(z->value.integer == c3.i); + z = cxJsonObjGet(x, "name"); + CX_TEST_ASSERT(cxJsonIsString(z)); + CX_TEST_ASSERT(!cx_strcmp(z->value.string, c3.name)); + + cxJsonValueFree(value); + } +}
--- a/test/json.h Wed Dec 10 18:36:02 2025 +0100 +++ b/test/json.h Thu Dec 11 22:58:02 2025 +0100 @@ -42,6 +42,7 @@ CX_TEST(testObjectToJsonSimple); CX_TEST(testObjectToJsonChildObj); +CX_TEST(testObjectToJsonChildList); #ifdef __cplusplus
--- a/test/main.c Wed Dec 10 18:36:02 2025 +0100 +++ b/test/main.c Thu Dec 11 22:58:02 2025 +0100 @@ -136,6 +136,7 @@ #endif cx_test_register(suite, testObjectToJsonSimple); cx_test_register(suite, testObjectToJsonChildObj); + cx_test_register(suite, testObjectToJsonChildList); cx_test_run_stdout(suite);