implement json primitives serialization

Tue, 09 Dec 2025 18:24:48 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 09 Dec 2025 18:24:48 +0100
changeset 26
dc36aa437249
parent 25
0bb91d1f9bba
child 27
5baee70feaa9

implement json primitives serialization

dbutils/Makefile file | annotate | diff | comparison | revisions
dbutils/dbutils/dbutils.h file | annotate | diff | comparison | revisions
dbutils/dbutils/json.h file | annotate | diff | comparison | revisions
dbutils/field.c file | annotate | diff | comparison | revisions
dbutils/json.c file | annotate | diff | comparison | revisions
dbutils/json.h file | annotate | diff | comparison | revisions
test/Makefile file | annotate | diff | comparison | revisions
test/database.c file | annotate | diff | comparison | revisions
test/database.h 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
testdata.sql file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
--- a/dbutils/Makefile	Tue Dec 09 14:47:35 2025 +0100
+++ b/dbutils/Makefile	Tue Dec 09 18:24:48 2025 +0100
@@ -35,6 +35,7 @@
 SRC += db.c
 SRC += object.c
 SRC += sqlite.c
+SRC += json.c
 
 OBJ = $(SRC:%.c=../build/dbutils/%$(OBJ_EXT))
 
--- a/dbutils/dbutils/dbutils.h	Tue Dec 09 14:47:35 2025 +0100
+++ b/dbutils/dbutils/dbutils.h	Tue Dec 09 18:24:48 2025 +0100
@@ -54,6 +54,12 @@
 
 typedef int(*DBUFieldDefInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj);
 typedef int(*DBUFieldInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj, const char *value, size_t length);
+typedef bool(*DBUFieldToBoolFunc)(DBUField *f, DBUObject obj);
+typedef int64_t(*DBUFieldToInt64Func)(DBUField *f, DBUObject obj);
+typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj);
+typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj);
+typedef cxmutstr(*DBUFieldToStringFunc)(DBUField *f, DBUObject obj, const CxAllocator *a);
+typedef double(*DBUFieldToDoubleFunc)(DBUField *f, DBUObject obj);
 
 struct DBUContext {
     /*
@@ -180,6 +186,49 @@
      */
     int (*initObjValue)(DBUField *f, const CxAllocator *a, DBUObject obj, void *child);
     
+    
+    /*
+     * get the field value as a boolean
+     * 
+     * This should only be implemented, if the field is a boolean
+     */
+    bool (*toBool)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as an int64 (optional)
+     * 
+     * This should only be implemented, if the field is a signed integer
+     */
+    int64_t (*toInt64)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as an int64 (optional)
+     * 
+     * This should only be implemented, if the field is a unsigned integer
+     */
+    uint64_t (*toUInt64)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as a string(optional)
+     * 
+     * This should only be implemented, if the field is represented as a string
+     */
+    cxmutstr (*toString)(DBUField *f, DBUObject obj, const CxAllocator *a);
+    
+    /*
+     * get the field value as a binary (optional)
+     * 
+     * This should only be implemented, if the field contains binary data
+     */
+    cxmutstr (*toBinary)(DBUField *f, DBUObject obj, const CxAllocator *a);
+    
+    /*
+     * get the field value as double (optional)
+     * 
+     * This should only be implemented, if the field is a float/double
+     */
+    double (*toDouble)(DBUField *f, DBUObject obj);
+    
     /*
      * destructor (optional)
      * 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbutils/dbutils/json.h	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBDBU_JSON_H
+#define LIBDBU_JSON_H
+
+#include <cx/json.h>
+#include "dbutils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a);
+cxmutstr dbuObjectToJsonString(DBUClass *type, void *obj, const CxAllocator *a);
+
+CxJsonValue* dbuListToJson(DBUClass *type, CxList *list, const CxAllocator *a);
+CxJsonValue* dbuArrayToJson(DBUClass *type, void **objArray, size_t size, const CxAllocator *a);
+
+cxmutstr dbuListToJsonString(DBUClass *type, CxList *list, const CxAllocator *a);
+cxmutstr dbuArrayToJsonString(DBUClass *type, void **objArray, size_t size, const CxAllocator *a);
+
+CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBDBU_JSON_H */
+
--- a/dbutils/field.c	Tue Dec 09 14:47:35 2025 +0100
+++ b/dbutils/field.c	Tue Dec 09 18:24:48 2025 +0100
@@ -177,6 +177,11 @@
     return 0;
 }
 
+static int64_t field_get_int8(DBUOffsetField *f, DBUObject obj) {
+    int8_t *i = (int8_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint8(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -195,6 +200,11 @@
     return 0;
 }
 
+static uint64_t field_get_uint8(DBUOffsetField *f, DBUObject obj) {
+    uint8_t *i = (uint8_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_int16(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -213,6 +223,11 @@
     return 0;
 }
 
+static int64_t field_get_int16(DBUOffsetField *f, DBUObject obj) {
+    int16_t *i = (int16_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint16(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -231,6 +246,11 @@
     return 0;
 }
 
+static uint64_t field_get_uint16(DBUOffsetField *f, DBUObject obj) {
+    uint16_t *i = (uint16_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_int32(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -249,6 +269,11 @@
     return 0;
 }
 
+static int64_t field_get_int32(DBUOffsetField *f, DBUObject obj) {
+    int32_t *i = (int32_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint32(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -267,6 +292,12 @@
     return 0;
 }
 
+static uint64_t field_get_uint32(DBUOffsetField *f, DBUObject obj) {
+    uint32_t *i = (uint32_t*)(obj + f->offset);
+    return *i;
+}
+
+
 static int field_init_int64(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -282,6 +313,11 @@
     return 0;
 }
 
+static int64_t field_get_int64(DBUOffsetField *f, DBUObject obj) {
+    int64_t *i = (int64_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint64(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -297,6 +333,11 @@
     return 0;
 }
 
+static uint64_t field_get_uint64(DBUOffsetField *f, DBUObject obj) {
+    uint64_t *i = (uint64_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_size(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -308,10 +349,15 @@
     if(!str2uint(value, &i)) {
         return 1;
     }
-    *(uint64_t*)(obj+f->offset) = i;
+    *(size_t*)(obj+f->offset) = i;
     return 0;
 }
 
+static uint64_t field_get_size(DBUOffsetField *f, DBUObject obj) {
+    size_t *i = (size_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_ssize(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -327,6 +373,11 @@
     return 0;
 }
 
+static int64_t field_get_ssize(DBUOffsetField *f, DBUObject obj) {
+    ssize_t *i = (ssize_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_bool(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -345,6 +396,11 @@
     return 0;
 }
 
+static bool field_get_bool(DBUOffsetField *f, DBUObject obj) {
+    bool *i = (bool*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_float(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -361,6 +417,11 @@
     return 0;
 }
 
+static double field_get_float(DBUOffsetField *f, DBUObject obj) {
+    float *d = (float*)(obj + f->offset);
+    return *d;
+}
+
 static int field_init_double(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -377,6 +438,10 @@
     return 0;
 }
 
+static double field_get_double(DBUOffsetField *f, DBUObject obj) {
+    double *d = (double*)(obj + f->offset);
+    return *d;
+}
 
 static int field_init_str(
         DBUOffsetField *f,
@@ -393,6 +458,12 @@
     return 0;
 }
 
+
+static cxmutstr field_get_str(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) {
+    char **s = (char**)(obj + f->offset);
+    return cx_strdup_a(a, cx_str(*s));
+}
+
 static int field_init_cxmutstr(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -408,6 +479,11 @@
     return 0;
 }
 
+static cxmutstr field_get_cxstr(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) {
+    cxmutstr *s = (cxmutstr*)(obj + f->offset);
+    return cx_strdup_a(a, *s);
+}
+
 static int field_init_str_size(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -424,6 +500,12 @@
     return 0;
 }
 
+static cxmutstr field_get_str_len(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) {
+    char *ptr = *(char**)(obj+f->offset_obj);
+    size_t len = *(size_t*)(obj+f->offset_len);
+    return cx_strdup_a(a, cx_strn(ptr, len));
+}
+
 static int field_init_str_intlen(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -440,6 +522,13 @@
     return 0;
 }
 
+static cxmutstr field_get_str_intlen(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) {
+    char *ptr = *(char**)(obj+f->offset_obj);
+    int len = *(int*)(obj+f->offset_len);
+    return cx_strdup_a(a, cx_strn(ptr, len));
+}
+
+
 static int field_init_bytes_size(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -494,7 +583,7 @@
     return (DBUField*)field;
 }
 
-static void add_offset_def_field(
+static DBUField* add_offset_def_field(
         DBUClass *cls, 
         const char *name, 
         off_t offset,
@@ -503,10 +592,12 @@
         bool nonnull, 
         union DBUDefValue def)
 {
-    dbuClassAddField(cls, name, create_offset_def_field(offset, def_init, init, nonnull, def));
+    DBUField *field = create_offset_def_field(offset, def_init, init, nonnull, def);
+    dbuClassAddField(cls, name, field);
+    return field;
 }
 
-static void add_offset_fk_field(
+static DBUField* add_offset_fk_field(
         DBUClass *cls, 
         const char *name, 
         off_t offset,
@@ -514,7 +605,9 @@
         DBUFieldInitFunc init,
         bool nonnull)
 {
-    dbuClassAddFKField(cls, name, create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 }), fkcls);
+    DBUField *field = create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 });
+    dbuClassAddFKField(cls, name, field, fkcls);
+    return field;
 }
 
 static void add_bool(
@@ -530,6 +623,7 @@
     field->field.query_length = false;
     field->offset = offset;
     field->def.def = 0;
+    field->field.toInt64 = (DBUFieldToInt64Func)field_get_bool;
     dbuClassAddField(cls, name, (DBUField*)field);
 }
 
@@ -548,24 +642,19 @@
     return (DBUField*)field;
 }
 
-static void add_offset_str_field(
+static DBUField* add_offset_str_field(
         DBUClass *cls, 
         const char *name,
         DBUFieldInitFunc init,
         off_t offset,
         bool nonnull)
 {
-    DBUOffsetField *field = calloc(1, sizeof(DBUOffsetField));
-    field->field.initDefaultValue = NULL;
-    field->field.initValue = (DBUFieldInitFunc)init;
-    field->field.nonnull = nonnull;
-    field->field.query_length = true;
-    field->offset = offset;
-    field->def.def = 0;
-    dbuClassAddField(cls, name, create_offset_str_field(init, offset, nonnull));
+    DBUField *f = create_offset_str_field(init, offset, nonnull);
+    dbuClassAddField(cls, name, f);
+    return f;
 }
 
-static void add_objlen_field(
+static DBUField* add_objlen_field(
         DBUClass *cls, 
         const char *name, 
         DBUFieldInitFunc init,
@@ -582,40 +671,53 @@
     field->offset_obj = offset;
     field->offset_len = size_offset;
     dbuClassAddField(cls, name, (DBUField*)field);
+    return (DBUField*)field;
 }
 
 
 
 
 DBUField* dbuFieldCreateInt32(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
+    return f;
 }
 
 DBUField* dbuFieldCreateUInt32(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
+    return f;
 }
 
 DBUField* dbuFieldCreateInt64(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
+    return f;
 }
 
 DBUField* dbuFieldCreateUInt64(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
+    return f;
 }
 
 DBUField* dbuFieldCreateString(off_t offset) {
-    return create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false);
+    DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false);
+    f->toString = (DBUFieldToStringFunc)field_get_str;
+    return f;
 }
 
 DBUField* dbuFieldCreateCxMutStr(off_t offset) {
-    return create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false);
+    DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false);
+    f->toString = (DBUFieldToStringFunc)field_get_cxstr;
+    return f;
 }
 
 /* --------------------            PUBLIC            -------------------- */
 
 
 void dbuClassAddInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -623,10 +725,11 @@
             (DBUFieldInitFunc)field_init_int,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -634,10 +737,11 @@
             (DBUFieldInitFunc)field_init_uint,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -645,10 +749,11 @@
             (DBUFieldInitFunc)field_init_int8,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int8;
 }
 
 void dbuClassAddUInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -656,10 +761,11 @@
             (DBUFieldInitFunc)field_init_uint8,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint8;
 }
 
 void dbuClassAddInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -667,10 +773,11 @@
             (DBUFieldInitFunc)field_init_int16,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int16;
 }
 
 void dbuClassAddUInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -678,10 +785,11 @@
             (DBUFieldInitFunc)field_init_uint16,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint16;
 }
 
 void dbuClassAddInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -689,10 +797,11 @@
             (DBUFieldInitFunc)field_init_int32,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -700,10 +809,11 @@
             (DBUFieldInitFunc)field_init_uint32,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -711,10 +821,11 @@
             (DBUFieldInitFunc)field_init_int64,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
 }
 
 void dbuClassAddUInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -722,10 +833,11 @@
             (DBUFieldInitFunc)field_init_uint64,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
 }
 
 void dbuClassAddSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -733,10 +845,11 @@
             (DBUFieldInitFunc)field_init_size,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_size;
 }
 
 void dbuClassAddSSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -744,6 +857,7 @@
             (DBUFieldInitFunc)field_init_ssize,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_ssize;
 }
 
 void dbuClassAddBool(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
@@ -755,7 +869,7 @@
 }
 
 void dbuClassAddFloat(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -763,10 +877,11 @@
             (DBUFieldInitFunc)field_init_float,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_float;
 }
 
 void dbuClassAddDouble(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -774,64 +889,71 @@
             (DBUFieldInitFunc)field_init_double,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_double;
 }
 
 void dbuClassAddString(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_str_field(
+    DBUField *f = add_offset_str_field(
             cls, 
             name,
             (DBUFieldInitFunc)field_init_str,
             offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str;
 }
 
 void dbuClassAddCXMutStr(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_str_field(
+    DBUField *f = add_offset_str_field(
             cls, 
             name,
             (DBUFieldInitFunc)field_init_cxmutstr,
             offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_cxstr;
 }
 
 void dbuClassAddStringSize(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_str_size,
             offset,
             size_offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str_len;
 }
 
 void dbuClassAddStringIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_str_intlen,
             offset,
             int_offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str_intlen;
 }
 
 void dbuClassAddBuf(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_bytes_size,
             offset,
             size_offset,
             nonnull);
+    f->toBinary = (DBUFieldToStringFunc)field_get_str_len;
 }
 
 void dbuClassAddBufIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_bytes_intlen,
             offset,
             int_offset,
             nonnull);
+    f->toBinary = (DBUFieldToStringFunc)field_get_str_intlen;
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbutils/json.c	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,97 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 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 "json.h"
+
+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);
+    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;
+            }
+        } 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;
+        }
+    }
+    
+    return value;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbutils/json.h	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 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.
+ */
+
+#ifndef DBU_JSON_H
+#define DBU_JSON_H
+
+#include "dbutils/json.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DBU_JSON_H */
+
--- a/test/Makefile	Tue Dec 09 14:47:35 2025 +0100
+++ b/test/Makefile	Tue Dec 09 18:24:48 2025 +0100
@@ -33,6 +33,7 @@
 
 SRC  = main.c
 SRC += database.c
+SRC += json.c
 
 OBJ = $(SRC:%.c=../build/test/%$(OBJ_EXT))
 
@@ -42,7 +43,7 @@
 all: ../build/bin/test
 
 $(TESTBIN): $(OBJ) $(LIBDBUTILS)
-	$(CC) -o $(TESTBIN) $(OBJ) -L$(BUILD_ROOT)/build/lib -ldbutils -lucx $(LDFLAGS) $(DBU_LDFLAGS)
+	$(CC) -o $(TESTBIN) $(OBJ) -L$(BUILD_ROOT)/build/lib -ldbutils ../build/lib/libucx.a $(LDFLAGS) $(DBU_LDFLAGS)
 
 ../build/test/%$(OBJ_EXT): %.c
 	$(CC) $(CFLAGS) $(DBU_CFLAGS) -o $@ -c $<
--- a/test/database.c	Tue Dec 09 14:47:35 2025 +0100
+++ b/test/database.c	Tue Dec 09 18:24:48 2025 +0100
@@ -253,6 +253,8 @@
         CX_TEST_ASSERT(!cx_strcmp(a1->zip, "23456"));
         CX_TEST_ASSERT(!cx_strcmp(a0->city, "city 17"));
         CX_TEST_ASSERT(!cx_strcmp(a1->city, "city 18"));
+        
+        dbuObjectBuilderDestroy(builder);
     }
     
     cxMempoolFree(mp);
--- a/test/database.h	Tue Dec 09 14:47:35 2025 +0100
+++ b/test/database.h	Tue Dec 09 18:24:48 2025 +0100
@@ -41,9 +41,7 @@
 #endif
    
 int init_test_db(void);
-
 int init_db_tests(void);
-
 void cleanup_db_tests(void);
 
 CX_TEST(testSqliteConnection);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/json.c	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,138 @@
+/*
+ * 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
+ * POSSIBLIITY OF SUCH DAMAGE.
+ */
+
+#include "json.h"
+
+#include <stdbool.h>
+#include <cx/buffer.h>
+
+
+typedef struct Test1 {
+    char *str;
+    cxmutstr str2;
+    int8_t i8;
+    uint8_t u8;
+    int64_t i64;
+    int16_t i16;
+    int32_t i32;
+    uint32_t u32;
+    uint16_t u16;
+    uint64_t u64;
+    bool bt;
+    bool bf;
+    double d;
+} Test1;
+
+static DBUContext *ctx;
+static DBUClass *test1_class;
+
+int init_json_tests(void) {
+    ctx = dbuContextCreate();
+    
+    test1_class = dbuRegisterClassWithoutPK(ctx, "test1", sizeof(Test1));
+    dbuClassAdd(test1_class, Test1, str);
+    dbuClassAdd(test1_class, Test1, str2);
+    dbuClassAdd(test1_class, Test1, i8);
+    dbuClassAdd(test1_class, Test1, u8);
+    dbuClassAdd(test1_class, Test1, i16);
+    dbuClassAdd(test1_class, Test1, u16);
+    dbuClassAdd(test1_class, Test1, i32);
+    dbuClassAdd(test1_class, Test1, u32);
+    dbuClassAdd(test1_class, Test1, i64);
+    dbuClassAdd(test1_class, Test1, u64);
+    dbuClassAdd(test1_class, Test1, bt);
+    dbuClassAdd(test1_class, Test1, bf);
+    dbuClassAdd(test1_class, Test1, d);
+}
+
+void cleanup_json_tests(void) {
+    dbuContextFree(ctx);
+}
+
+CX_TEST(testObjectToJsonSimple) {
+    Test1 test;
+    test.str = "hello str";
+    test.str2 = cx_mutstr("hello cx str");
+    test.i8 = 32;
+    test.u8 = 250;
+    test.i16 = -5533;
+    test.u16 = 8000;
+    test.i32 = -123456;
+    test.u32 = 4000000000;
+    test.i64 = -999999999999L;
+    test.u64 = 2501;
+    test.bt = true;
+    test.bf = false;
+    test.d = 431.15;
+    
+    CX_TEST_DO {
+        CxJsonValue *value = dbuObjectToJson(test1_class, &test, NULL);
+        
+        CX_TEST_ASSERT(value);
+        CX_TEST_ASSERT(value->type == CX_JSON_OBJECT);
+        
+        CxJsonValue *str = cxJsonObjGet(value, "str");
+        CxJsonValue *str2 = cxJsonObjGet(value, "str2");
+        CxJsonValue *i8 = cxJsonObjGet(value, "i8");
+        CxJsonValue *u8 = cxJsonObjGet(value, "u8");
+        CxJsonValue *i16 = cxJsonObjGet(value, "i16");
+        CxJsonValue *u16 = cxJsonObjGet(value, "u16");
+        CxJsonValue *i32 = cxJsonObjGet(value, "i32");
+        CxJsonValue *u32 = cxJsonObjGet(value, "u32");
+        CxJsonValue *i64 = cxJsonObjGet(value, "i64");
+        CxJsonValue *u64 = cxJsonObjGet(value, "u64");
+        CxJsonValue *bt = cxJsonObjGet(value, "bt");
+        CxJsonValue *bf = cxJsonObjGet(value, "bf");
+        CxJsonValue *d = cxJsonObjGet(value, "d");
+        
+        CX_TEST_ASSERT(str && str->type == CX_JSON_STRING);
+        CX_TEST_ASSERT(str2 && str2->type == CX_JSON_STRING);
+        CX_TEST_ASSERT(i8 && i8->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(u8 && u8->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(i16 && i16->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(u16 && u16->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(i32 && i32->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(u32 && u32->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(i64 && i64->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(u64 && u64->type == CX_JSON_INTEGER);
+        CX_TEST_ASSERT(d && d->type == CX_JSON_NUMBER);
+        
+        CX_TEST_ASSERT(!cx_strcmp(str->value.string, test.str));
+        CX_TEST_ASSERT(!cx_strcmp(str2->value.string, test.str2));
+        CX_TEST_ASSERT(i8->value.integer == test.i8);
+        CX_TEST_ASSERT(u8->value.integer == test.u8);
+        CX_TEST_ASSERT(i16->value.integer == test.i16);
+        CX_TEST_ASSERT(u16->value.integer == test.u16);
+        CX_TEST_ASSERT(i32->value.integer == test.i32);
+        CX_TEST_ASSERT(u32->value.integer == test.u32);
+        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);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/json.h	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * POSSIBLIITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEST_JSON_H
+#define TEST_JSON_H
+
+#include <dbutils/json.h>
+
+#include <cx/test.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int init_json_tests(void);
+void cleanup_json_tests(void);
+
+CX_TEST(testObjectToJsonSimple);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEST_JSON_H */
+
--- a/test/main.c	Tue Dec 09 14:47:35 2025 +0100
+++ b/test/main.c	Tue Dec 09 18:24:48 2025 +0100
@@ -38,6 +38,7 @@
 #include <cx/printf.h>
 
 #include "database.h"
+#include "json.h"
 
 const char *sql_create_table_person =
 "create table if not exists Person ("
@@ -120,6 +121,7 @@
 int main(int argc, char **argv) {
     CxTestSuite *suite = cx_test_suite_new("dbu");
     
+    init_json_tests();
 #ifdef DBU_SQLITE
     if(init_test_db() || init_db_tests()) {
         return 1;
@@ -132,6 +134,7 @@
     cx_test_register(suite, testSqlExecParam);
     cx_test_register(suite, testSingleTableQuery);
 #endif
+    cx_test_register(suite, testObjectToJsonSimple);
     
     cx_test_run_stdout(suite);
     
@@ -139,6 +142,7 @@
 #ifdef DBU_SQLITE
     cleanup_db_tests();
 #endif
+    cleanup_json_tests();
     
     /*
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata.sql	Tue Dec 09 18:24:48 2025 +0100
@@ -0,0 +1,40 @@
+
+create table Person(
+	person_id integer primary key autoincrement,
+	name text,
+	email text,
+	age integer,
+	iscustomer integer,
+	hash integer,
+	address_id integer
+);
+
+create table Address(
+	address_id integer primary key autoincrement,
+	street text,
+	zip text,
+	city text
+);
+
+create table Role(
+	role_id integer primary key autoincrement,
+	person_id integer,
+	name text
+);
+
+insert into address (street, zip, city) 
+values 
+('street 1', '12343', 'city 17'), 
+('street 2', '23456', 'city 18');
+
+insert into person (name, email, age, iscustomer, hash, address_id) values
+('alice', 'alice@example.com', 30, 0, 123456789, (select address_id from address where street = 'street 1')),
+('bob', 'bob@example.com', 25, 1, 987654321,  (select address_id from address where street = 'street 2'));
+
+insert into role (person_id, name) 
+values 
+(1, 'finance'), 
+(1, 'dev'), 
+(1, 'manager'), 
+(2, 'extern');
+
--- a/ucx/Makefile	Tue Dec 09 14:47:35 2025 +0100
+++ b/ucx/Makefile	Tue Dec 09 18:24:48 2025 +0100
@@ -38,6 +38,7 @@
 SRC += hash_map.c
 SRC += iterator.c
 SRC += linked_list.c
+SRC += kv_list.c
 SRC += list.c
 SRC += map.c
 SRC += printf.c
@@ -46,21 +47,19 @@
 SRC += streams.c
 SRC += properties.c
 SRC += json.c
-SRC += kv_list.c
-
 
 OBJ   = $(SRC:%.c=../build/ucx/%$(OBJ_EXT))
 
-UCX_LIB = ../build/lib/libucx$(LIB_EXT)
+UCX_LIB = ../build/lib/libucx.a
 
-all: ../build/ucx $(UCX_LIB)
+all: $(UCX_LIB) $(UCX_SHLIB)
 
 $(UCX_LIB): $(OBJ)
-	$(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ)
+	$(AR) $(ARFLAGS) $@ $(OBJ)
 
 ../build/ucx:
 	mkdir -p ../build/ucx
 
 ../build/ucx/%$(OBJ_EXT): %.c
-	$(CC) $(CFLAGS) -o $@ -c $<
+	$(CC) $(CFLAGS) $(SHLIB_CFLAGS) -o $@ -c $<
 

mercurial