fix cxJsonFromString() ignoring unexpected trailing data + fix wrong handling of trailing whitespaces in general

Wed, 10 Dec 2025 22:53:41 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 10 Dec 2025 22:53:41 +0100
changeset 1567
f60f23b362e9
parent 1566
2ebbcb38986d
child 1568
355066cd9186

fix cxJsonFromString() ignoring unexpected trailing data + fix wrong handling of trailing whitespaces in general

resolves #777

src/json.c file | annotate | diff | comparison | revisions
tests/test_json.c file | annotate | diff | comparison | revisions
--- a/src/json.c	Wed Dec 10 22:34:48 2025 +0100
+++ b/src/json.c	Wed Dec 10 22:53:41 2025 +0100
@@ -228,7 +228,9 @@
         }
     }
 
-    if (ttype != CX_JSON_NO_TOKEN) {
+    if (ttype == CX_JSON_NO_TOKEN) {
+        return CX_JSON_NO_DATA;
+    } else {
         // uncompleted token
         size_t uncompleted_len = json->buffer.size - token_part_start;
         if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
@@ -255,9 +257,8 @@
         }
         // advance the buffer position - we saved the stuff in the uncompleted token
         json->buffer.pos += uncompleted_len;
+        return CX_JSON_INCOMPLETE_DATA;
     }
-
-    return CX_JSON_INCOMPLETE_DATA;
 }
 
 // converts a Unicode codepoint to utf8
@@ -804,8 +805,23 @@
         // LCOV_EXCL_STOP
     }
     CxJsonStatus status = cxJsonNext(&parser, value);
+    // check if we consume the total string
+    CxJsonValue *chk_value = NULL;
+    CxJsonStatus chk_status = CX_JSON_NO_DATA;
+    if (status == CX_JSON_NO_ERROR) {
+        chk_status = cxJsonNext(&parser, &chk_value);
+    }
     cxJsonDestroy(&parser);
-    return status;
+    if (chk_status == CX_JSON_NO_DATA) {
+        return status;
+    } else {
+        cxJsonValueFree(*value);
+        // if chk_value is nothing, the free is harmless
+        cxJsonValueFree(chk_value);
+        *value = &cx_json_value_nothing;
+        return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
+    }
+
 }
 
 void cxJsonValueFree(CxJsonValue *value) {
--- a/tests/test_json.c	Wed Dec 10 22:34:48 2025 +0100
+++ b/tests/test_json.c	Wed Dec 10 22:53:41 2025 +0100
@@ -215,23 +215,34 @@
 
 CX_TEST(test_json_from_string_multiple_values) {
     CxJsonStatus status;
-    CxJsonValue *obj = NULL;
+    CxJsonValue *obj;
     CX_TEST_DO {
+        obj = NULL;
         status = cxJsonFromString(NULL, "{ \"obj1\": \"hello\" }\n\"value2\"\n", &obj);
-        
-        // TODO: what is the expected behavior here? Is this an error or do we ignore the second value?
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+        CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+
+        obj = NULL;
+        status = cxJsonFromString(NULL, "\"value\" \n ] syntax error [", &obj);
+        CX_TEST_ASSERT(obj != NULL);
+        CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING);
+        CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+    }
+}
+
+CX_TEST(test_json_from_string_untrimmed) {
+    CxJsonStatus status;
+    CxJsonValue *obj;
+    CX_TEST_DO {
+        obj = NULL;
+        status = cxJsonFromString(NULL, "\n\t{ \"obj1\": \"hello\" }   \n", &obj);
         CX_TEST_ASSERT(status == CX_JSON_NO_ERROR);
         CX_TEST_ASSERT(cxJsonIsObject(obj));
-        // CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
-        
-        cxJsonValueFree(obj);
-        
-        // TODO: this really should be an error in theory
-        status = cxJsonFromString(NULL, "\"value\" \n ] syntax error [", &obj);
-        CX_TEST_ASSERT(status == CX_JSON_NO_ERROR);
-        CX_TEST_ASSERT(cxJsonIsString(obj));
-        // CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
-        
+        CxJsonValue *obj1 = cxJsonObjGet(obj, "obj1");
+        CX_TEST_ASSERT(cxJsonIsString(obj1));
+        CX_TEST_ASSERT(cx_strcmp(cxJsonAsCxString(obj1), "hello") == 0);
+
         cxJsonValueFree(obj);
     }
 }
@@ -930,7 +941,7 @@
         cxJsonValueFree(v);
         // read remaining '\n'
         result = cxJsonNext(&json, &v);
-        CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA);
+        CX_TEST_ASSERT(result == CX_JSON_NO_DATA);
         // read string
         cxJsonFill(&json, "\"hello world\"\n");
         result = cxJsonNext(&json, &v);
@@ -1565,6 +1576,7 @@
     cx_test_register(suite, test_json_from_string);
     cx_test_register(suite, test_json_from_string_errors);
     cx_test_register(suite, test_json_from_string_multiple_values);
+    cx_test_register(suite, test_json_from_string_untrimmed);
     cx_test_register(suite, test_json_escaped_strings);
     cx_test_register(suite, test_json_escaped_unicode_strings);
     cx_test_register(suite, test_json_escaped_unicode_malformed);

mercurial