# HG changeset patch # User Olaf Wintermann # Date 1765388162 -3600 # Node ID e46f9f254fcdaf39dfecfac2dc4da8676f31fdb0 # Parent 5baee70feaa9179e667edf3317d6c9d45cca5186 implement json child object serialization diff -r 5baee70feaa9 -r e46f9f254fcd dbutils/dbutils/dbutils.h --- a/dbutils/dbutils/dbutils.h Tue Dec 09 19:40:08 2025 +0100 +++ b/dbutils/dbutils/dbutils.h Wed Dec 10 18:36:02 2025 +0100 @@ -143,6 +143,39 @@ }; /* + * list abstraction for accessing elements + */ +typedef struct DBUAbstractList DBUAbstractList; +struct DBUAbstractList { + /* + * List pointer (for example a C array or CxList*) + */ + void *list; + /* + * Get the list size + */ + size_t (*length)(DBUAbstractList *ls); + /* + * Get the element at the specified index + */ + void* (*get)(DBUAbstractList *ls, size_t index); + /* + * Get an DBUField for an list element. The DBUField pointer is only + * valid until another elementField call. + */ + DBUField* (*elementField)(DBUAbstractList *ls, void *elm); + /* + * Optional function for getting an list iterator + */ + CxIterator (*iterator)(DBUAbstractList *ls); + /* + * Free the DBUAbstractList abstraction layer. The actual list is + * not freed. + */ + void (*free)(DBUAbstractList *ls); +}; + +/* * abstract field */ struct DBUField { @@ -152,6 +185,11 @@ cxmutstr name; /* + * field object type + */ + DBUClass *objType; + + /* * called, if the field is null (optional) */ int (*initDefaultValue)(DBUField *f, const CxAllocator *a, DBUObject obj); @@ -230,6 +268,21 @@ double (*toDouble)(DBUField *f, DBUObject obj); /* + * get the field value as object (optional) + * + * This should only be implemented, if the field has a compley type + * and objType is set. + */ + DBUObject (*toObject)(DBUField *f, DBUObject obj); + + /* + * get the field value as a list/iterator (optional) + * + * This should only be implemented, if the field is a list. + */ + DBUAbstractList (*toList)(DBUField *f); + + /* * destructor (optional) * * this callback must not free the DBUField* ptr diff -r 5baee70feaa9 -r e46f9f254fcd dbutils/field.c --- a/dbutils/field.c Tue Dec 09 19:40:08 2025 +0100 +++ b/dbutils/field.c Wed Dec 10 18:36:02 2025 +0100 @@ -1201,10 +1201,17 @@ return 0; } +static DBUObject get_obj_ptr(DBUField *f, DBUObject obj) { + DBUOffsetField *field = (DBUOffsetField*)f; + return *(void**)(obj+field->offset); +} + void dbuClassAddObj(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls) { DBUOffsetField *field = malloc(sizeof(DBUOffsetField)); memset(field, 0, sizeof(DBUOffsetField)); field->field.initObjValue = set_obj_ptr; + field->field.toObject = get_obj_ptr; + field->field.objType = foreign_cls; field->offset = offset; field->def.def = 0; dbuClassAddObjField(cls, name, (DBUField*)field, foreign_cls); diff -r 5baee70feaa9 -r e46f9f254fcd dbutils/json.c --- a/dbutils/json.c Tue Dec 09 19:40:08 2025 +0100 +++ b/dbutils/json.c Wed Dec 10 18:36:02 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2024 Olaf Wintermann. All rights reserved. + * 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: @@ -47,50 +47,62 @@ // add all primitive values CxMapIterator i = cxMapIteratorValues(type->fields); - 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; + 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->value.string = s; } - child->allocator = a; - child->type = CX_JSON_STRING; - child->value.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) { + // TODO + } else { + continue; // non-serializable field } - } else if(field->toBinary) { - // TODO - } else { - continue; // non-serializable field + + if(!child) { + cxJsonValueFree(value); + return NULL; + } + + if(cxJsonObjPut(value, field->name, child)) { + cxJsonValueFree(child); + cxJsonValueFree(value); + return NULL; + } } - - 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; diff -r 5baee70feaa9 -r e46f9f254fcd dbutils/json.h --- a/dbutils/json.h Tue Dec 09 19:40:08 2025 +0100 +++ b/dbutils/json.h Wed Dec 10 18:36:02 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2024 Olaf Wintermann. All rights reserved. + * 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: diff -r 5baee70feaa9 -r e46f9f254fcd test/json.c --- a/test/json.c Tue Dec 09 19:40:08 2025 +0100 +++ b/test/json.c Wed Dec 10 18:36:02 2025 +0100 @@ -48,8 +48,27 @@ double d; } Test1; +typedef struct Test2 { + char *name; + int i; +} Test2; + +typedef struct Test3 { + char *test3; + Test2 *test2; +} Test3; + +typedef struct Test4 { + int id; + Test3 *test3; + Test2 *test2; +} Test4; + static DBUContext *ctx; static DBUClass *test1_class; +static DBUClass *test2_class; +static DBUClass *test3_class; +static DBUClass *test4_class; int init_json_tests(void) { ctx = dbuContextCreate(); @@ -68,6 +87,19 @@ dbuClassAdd(test1_class, Test1, bt); dbuClassAdd(test1_class, Test1, bf); dbuClassAdd(test1_class, Test1, d); + + test2_class = dbuRegisterClassWithoutPK(ctx, "test2", sizeof(Test2)); + dbuClassAdd(test2_class, Test2, name); + dbuClassAdd(test2_class, Test2, i); + + test3_class = dbuRegisterClassWithoutPK(ctx, "test3", sizeof(Test3)); + dbuClassAdd(test3_class, Test3, test3); + dbuClassAddObj(test3_class, "test2", offsetof(Test3, test2), test2_class); + + test4_class = dbuRegisterClassWithoutPK(ctx, "test4", sizeof(Test4)); + dbuClassAdd(test4_class, Test4, id); + dbuClassAddObj(test4_class, "test2", offsetof(Test4, test2), test2_class); + dbuClassAddObj(test4_class, "test3", offsetof(Test4, test3), test3_class); } void cleanup_json_tests(void) { @@ -94,7 +126,7 @@ CxJsonValue *value = dbuObjectToJson(test1_class, &test, NULL); CX_TEST_ASSERT(value); - CX_TEST_ASSERT(value->type == CX_JSON_OBJECT); + CX_TEST_ASSERT(cxJsonIsObject(value)); CxJsonValue *str = cxJsonObjGet(value, "str"); CxJsonValue *str2 = cxJsonObjGet(value, "str2"); @@ -133,6 +165,77 @@ CX_TEST_ASSERT(i64->value.integer == test.i64); CX_TEST_ASSERT(u64->value.integer == test.u64); CX_TEST_ASSERT(d->value.number < test.d + 0.1 && d->value.number > test.d - 0.1); + + cxJsonValueFree(value); } } +CX_TEST(testObjectToJsonChildObj) { + Test2 t2_1; + t2_1.i = 12; + t2_1.name = "t2_1"; + + Test2 t2_2; + t2_2.i = 45; + t2_2.name = "t2_2"; + + Test3 t3; + t3.test3 = "t3"; + t3.test2 = &t2_1; + + Test4 t4; + t4.id = 15; + t4.test2 = &t2_2; + t4.test3 = &t3; + + CX_TEST_DO { + CxJsonValue *value = dbuObjectToJson(test4_class, &t4, NULL); + + CX_TEST_ASSERT(value); + CX_TEST_ASSERT(cxJsonIsObject(value)); + + CxJsonValue *v = cxJsonObjGet(value, "id"); + CX_TEST_ASSERT(v); + CX_TEST_ASSERT(cxJsonIsInteger(v)); + CX_TEST_ASSERT(v->value.integer == t4.id); + + v = cxJsonObjGet(value, "test2"); + CX_TEST_ASSERT(v); + CX_TEST_ASSERT(cxJsonIsObject(v)); + + CxJsonValue *s = cxJsonObjGet(v, "i"); + CX_TEST_ASSERT(s); + CX_TEST_ASSERT(cxJsonIsInteger(s)); + CX_TEST_ASSERT(s->value.integer == t2_2.i); + + s = cxJsonObjGet(v, "name"); + CX_TEST_ASSERT(s); + CX_TEST_ASSERT(cxJsonIsString(s)); + CX_TEST_ASSERT(!cx_strcmp(s->value.string, t2_2.name)); + + v = cxJsonObjGet(value, "test3"); + CX_TEST_ASSERT(v); + CX_TEST_ASSERT(cxJsonIsObject(v)); + + s = cxJsonObjGet(v, "test3"); + CX_TEST_ASSERT(s); + CX_TEST_ASSERT(cxJsonIsString(s)); + CX_TEST_ASSERT(!cx_strcmp(s->value.string, t3.test3)); + + s = cxJsonObjGet(v, "test2"); + CX_TEST_ASSERT(s); + CX_TEST_ASSERT(cxJsonIsObject(s)); + + CxJsonValue *x = cxJsonObjGet(s, "i"); + CX_TEST_ASSERT(x); + CX_TEST_ASSERT(cxJsonIsInteger(x)); + CX_TEST_ASSERT(x->value.integer == t2_1.i); + + x = cxJsonObjGet(s, "name"); + CX_TEST_ASSERT(x); + CX_TEST_ASSERT(cxJsonIsString(x)); + CX_TEST_ASSERT(!cx_strcmp(x->value.string, t2_1.name)); + + cxJsonValueFree(value); + } +} diff -r 5baee70feaa9 -r e46f9f254fcd test/json.h --- a/test/json.h Tue Dec 09 19:40:08 2025 +0100 +++ b/test/json.h Wed Dec 10 18:36:02 2025 +0100 @@ -41,6 +41,7 @@ void cleanup_json_tests(void); CX_TEST(testObjectToJsonSimple); +CX_TEST(testObjectToJsonChildObj); #ifdef __cplusplus diff -r 5baee70feaa9 -r e46f9f254fcd test/main.c --- a/test/main.c Tue Dec 09 19:40:08 2025 +0100 +++ b/test/main.c Wed Dec 10 18:36:02 2025 +0100 @@ -135,6 +135,7 @@ cx_test_register(suite, testSingleTableQuery); #endif cx_test_register(suite, testObjectToJsonSimple); + cx_test_register(suite, testObjectToJsonChildObj); cx_test_run_stdout(suite);