implement deserialization of json objects that contain arrays default tip

Sun, 04 Jan 2026 18:09:59 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 04 Jan 2026 18:09:59 +0100
changeset 43
73c1ced3a620
parent 42
3c989bc611c6

implement deserialization of json objects that contain arrays

dbutils/class.c file | annotate | diff | comparison | revisions
dbutils/dbutils/dbutils.h file | annotate | diff | comparison | revisions
dbutils/field.c file | annotate | diff | comparison | revisions
dbutils/json.c file | annotate | diff | comparison | revisions
dbutils/object.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/class.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/dbutils/class.c	Sun Jan 04 18:09:59 2026 +0100
@@ -81,6 +81,7 @@
     free(field->name.ptr);
     field->name = name ? cx_strdup(cx_str(name)) : (cxmutstr){NULL,0};
     cxMapPut(cls->obj_fields, foreign_cls->name, field);
+    cxMapPut(cls->fields, name, field);
 }
 
 
--- a/dbutils/dbutils/dbutils.h	Sun Jan 04 17:45:50 2026 +0100
+++ b/dbutils/dbutils/dbutils.h	Sun Jan 04 18:09:59 2026 +0100
@@ -130,6 +130,7 @@
 struct DBUObjectResult {
     void *userdata1;
     void *userdata2;
+    DBUClass *type;
     int int1;
     int int2;
     DBUObject (*create)(DBUObjectResult *result, DBUClass *type, const CxAllocator *a);
--- a/dbutils/field.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/dbutils/field.c	Sun Jan 04 18:09:59 2026 +0100
@@ -1238,7 +1238,7 @@
 }
 static int linkedlist_add(DBUObjectResult *result, DBUObject parent, DBUClass *type, void *obj, CxList *fk, const CxAllocator *a) {
     DBUOffsetField *field  = result->userdata1;
-    DBUClass *cls = result->userdata2;
+    DBUClass *cls = result->type;
     CxList **list = (CxList**)(parent+field->offset);
     if(!*list) {
         *list = cxLinkedListCreate(a, CX_STORE_POINTERS);
@@ -1294,13 +1294,14 @@
     memset(field, 0, sizeof(DBUOffsetField));
     field->offset = offset;
     field->def.def = 0;
+    field->field.builder.type = foreign_cls;
     field->field.builder.userdata1 = field;
     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);
+    dbuClassAddObjField(cls, name, (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/json.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/dbutils/json.c	Sun Jan 04 18:09:59 2026 +0100
@@ -220,15 +220,27 @@
                         free(obj); // TODO: improve obj cleanup
                         return NULL;
                     }
-                } else {
-                    *error = 4;
-                    free(obj); // TODO: improve obj cleanup
-                    return NULL;
                 }
                 break;
             }
             case CX_JSON_ARRAY: {
-                // TODO
+                if(field->builder.add) {
+                    for(int i=0;i<child->array.size;i++) {
+                        CxJsonValue *elm = child->array.data[i];
+                        if(cxJsonIsObject(elm)) {
+                            void *elm_obj = jsonToObj(field->builder.type, a, elm, depth+1, error);
+                            if(!elm_obj) {
+                                free(obj); // TODO: improve obj cleanup
+                                return NULL;
+                            }
+                            if(field->builder.add(&field->builder, obj, field->builder.type, elm_obj, NULL, a)) {
+                                free(obj); // TODO: improve obj cleanup
+                                *error = 3;
+                                return NULL;
+                            }
+                        }
+                    }
+                }
                 break;
             }
             default: break;
--- a/dbutils/object.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/dbutils/object.c	Sun Jan 04 18:09:59 2026 +0100
@@ -133,7 +133,8 @@
     }
     // TODO: the class needs a obj destructor func, because we need to be able
     //       to destroy partialy created lists
-    DBUObjectResult result = { 
+    DBUObjectResult result = {
+        .type = builder->resultType,
         .userdata1 = result_list,
         .userdata2 = malloc(builder->resultType->obj_size),
         .int1 = builder->resultType->obj_size,
@@ -306,12 +307,19 @@
                 }
                 if(remaining.length > 2) {
                     field = cxMapGet(fcls->fields, cx_strsubs(remaining, 2));
+                    // make sure the field is a normal field and not an obj/list field
+                    if(!field->initValue) {
+                        field = NULL;
+                    }
                 }
                 field_mapping[i].cls = fcls;
                 field_class = fcls;
             }
         } else {
             field = cxMapGet(field_class->fields, fieldname);
+            if(field && !field->initValue) {
+                field = NULL;
+            }
         }
         
         if(field) {
--- a/test/json.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/test/json.c	Sun Jan 04 18:09:59 2026 +0100
@@ -381,3 +381,37 @@
         free(obj);
     }
 }
+
+CX_TEST(testJsonToObjectWithArray) {
+    const char *jsonStr =
+        "{"
+        "\"test\":\"hello world\","
+        "\"test2List\":[ {\"name\":\"elm0\", \"i\":0}, {\"name\":\"elm1\", \"i\":1} ]"
+        "}";
+    
+    CxJsonValue *json;
+    cxJsonFromString(NULL, jsonStr, &json);
+    
+    CX_TEST_DO {
+        Test5 *obj = dbuJsonToObject(test5_class, NULL, json);
+        
+        CX_TEST_ASSERT(obj);
+        CX_TEST_ASSERT(!cx_strcmp(obj->test, "hello world"));
+        CX_TEST_ASSERT(obj->test2List);
+        CX_TEST_ASSERT(cxListSize(obj->test2List) == 2);
+        
+        Test2 *elm0 = cxListAt(obj->test2List, 0);
+        Test2 *elm1 = cxListAt(obj->test2List, 1);
+        
+        CX_TEST_ASSERT(elm0);
+        CX_TEST_ASSERT(elm1);
+        
+        CX_TEST_ASSERT(!cx_strcmp(elm0->name, "elm0"));
+        CX_TEST_ASSERT(elm0->i == 0);
+        
+        CX_TEST_ASSERT(!cx_strcmp(elm1->name, "elm1"));
+        CX_TEST_ASSERT(elm1->i == 1);
+        
+        free(obj);
+    }
+}
--- a/test/json.h	Sun Jan 04 17:45:50 2026 +0100
+++ b/test/json.h	Sun Jan 04 18:09:59 2026 +0100
@@ -46,6 +46,7 @@
 
 CX_TEST(testJsonToSimpleObject);
 CX_TEST(testJsonToObjectWithObjChild);
+CX_TEST(testJsonToObjectWithArray);
 
 
 #ifdef __cplusplus
--- a/test/main.c	Sun Jan 04 17:45:50 2026 +0100
+++ b/test/main.c	Sun Jan 04 18:09:59 2026 +0100
@@ -146,6 +146,7 @@
     cx_test_register(suite, testObjectToJsonChildList);
     cx_test_register(suite, testJsonToSimpleObject);
     cx_test_register(suite, testJsonToObjectWithObjChild);
+    cx_test_register(suite, testJsonToObjectWithArray);
     
     cx_test_run_stdout(suite);
     

mercurial