# HG changeset patch # User Mike Becker # Date 1736683472 -3600 # Node ID 2b83302d595a75b2c7ee8b63eda12aec190cccbe # Parent 49ab92de9a13fae677dac41e43e413049a39e62e make escaping slashes optional - fixes #569 diff -r 49ab92de9a13 -r 2b83302d595a src/cx/json.h --- a/src/cx/json.h Sat Jan 11 12:56:54 2025 +0100 +++ b/src/cx/json.h Sun Jan 12 13:04:32 2025 +0100 @@ -463,6 +463,10 @@ * Indentation is only used in pretty output. */ uint8_t indent; + /** + * Set true to enable escaping of the slash character (solidus). + */ + bool escape_slash; }; /** diff -r 49ab92de9a13 -r 2b83302d595a src/json.c --- a/src/json.c Sat Jan 11 12:56:54 2025 +0100 +++ b/src/json.c Sun Jan 12 13:04:32 2025 +0100 @@ -386,7 +386,7 @@ return result; } -static cxmutstr escape_string(cxmutstr str) { +static cxmutstr escape_string(cxmutstr str, bool escape_slash) { // note: this function produces the string without enclosing quotes // the reason is that we don't want to allocate memory just for that CxBuffer buf = {0}; @@ -396,8 +396,7 @@ bool escape = !isprint(str.ptr[i]) || str.ptr[i] == '\\' || str.ptr[i] == '"' - // TODO: make escaping slash optional - || str.ptr[i] == '/'; + || (escape_slash && str.ptr[i] == '/'); if (all_printable && escape) { size_t capa = str.length + 32; @@ -1058,7 +1057,8 @@ true, 6, false, - 4 + 4, + false }; } @@ -1068,7 +1068,8 @@ true, 6, use_spaces, - 4 + 4, + false }; } @@ -1149,7 +1150,7 @@ // the name actual += wfunc("\"", 1, 1, target); - cxmutstr name = escape_string(member->name); + cxmutstr name = escape_string(member->name, settings->escape_slash); actual += wfunc(name.ptr, 1, name.length, target); if (name.ptr != member->name.ptr) { cx_strfree(&name); @@ -1219,7 +1220,7 @@ } case CX_JSON_STRING: { actual += wfunc("\"", 1, 1, target); - cxmutstr str = escape_string(value->value.string); + cxmutstr str = escape_string(value->value.string, settings->escape_slash); actual += wfunc(str.ptr, 1, str.length, target); if (str.ptr != value->value.string.ptr) { cx_strfree(&str); diff -r 49ab92de9a13 -r 2b83302d595a tests/test_json.c --- a/tests/test_json.c Sat Jan 11 12:56:54 2025 +0100 +++ b/tests/test_json.c Sun Jan 12 13:04:32 2025 +0100 @@ -944,7 +944,7 @@ * According to RFC-8259 we have to test the following characters: * " quotation mark * \ reverse solidus - * / solidus + * / solidus ---> we make this optional, see test_json_write_solidus * b backspace * f form feed * n line feed @@ -954,14 +954,14 @@ * Also, all unicode characters are encoded that way - in our example the 'ö'. */ CxJsonValue* str = cxJsonCreateString(NULL, - "hello\twörld\r\nthis/is\\a \"string\"\b in \a string\f"); + "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f"); CxJsonWriter writer = cxJsonWriterCompact(); CxBuffer buf; cxBufferInit(&buf, NULL, 128, NULL, 0); CX_TEST_DO { cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), - CX_STR("\"hello\\tw\\u00c3\\u00b6rld\\r\\nthis\\/is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""))); + CX_STR("\"hello\\tw\\u00c3\\u00b6rld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""))); } cxBufferDestroy(&buf); cxJsonValueFree(str); @@ -970,19 +970,39 @@ CX_TEST(test_json_write_name_escape) { CxJsonValue* obj = cxJsonCreateObj(NULL); cxJsonObjPutLiteral(obj, - CX_STR("hello\twörld\r\nthis/is\\a \"string\"\b in \a string\f"), CX_JSON_TRUE); + CX_STR("hello\twörld\r\nthis is\\a \"string\"\b in \a string\f"), CX_JSON_TRUE); CxJsonWriter writer = cxJsonWriterCompact(); CxBuffer buf; cxBufferInit(&buf, NULL, 128, NULL, 0); CX_TEST_DO { cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer); CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), - CX_STR("{\"hello\\tw\\u00c3\\u00b6rld\\r\\nthis\\/is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"))); + CX_STR("{\"hello\\tw\\u00c3\\u00b6rld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"))); } cxBufferDestroy(&buf); cxJsonValueFree(obj); } +CX_TEST(test_json_write_solidus) { + CxJsonValue* str = cxJsonCreateString(NULL,"test/solidus"); + CxJsonWriter writer = cxJsonWriterCompact(); + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, NULL, 0); + CX_TEST_DO { + // default: do not escape + cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test/solidus\""))); + + // enable escaping + writer.escape_slash = true; + cxBufferReset(&buf); + cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test\\/solidus\""))); + } + cxBufferDestroy(&buf); + cxJsonValueFree(str); +} + CxTestSuite *cx_test_suite_json(void) { CxTestSuite *suite = cx_test_suite_new("json"); @@ -1008,6 +1028,7 @@ cx_test_register(suite, test_json_write_frac_max_digits); cx_test_register(suite, test_json_write_string_escape); cx_test_register(suite, test_json_write_name_escape); + cx_test_register(suite, test_json_write_solidus); return suite; }