implement list to json serialization

Thu, 11 Dec 2025 22:58:02 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 11 Dec 2025 22:58:02 +0100
changeset 29
b8c826c720f3
parent 28
e46f9f254fcd
child 30
d33eaaec15da

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);
     

mercurial