diff --git a/expected/pgproto_test.out b/expected/pgproto_test.out index b23b469..f5dbe88 100644 --- a/expected/pgproto_test.out +++ b/expected/pgproto_test.out @@ -569,3 +569,9 @@ ERROR: Field nonexistent not found in message CoverageMsg -- 35. Test pb_insert error: Message not found SELECT pb_insert('\x'::protobuf, ARRAY['NonExistentMessage', 'f', '0'], '1.23'); ERROR: Message not found in schema registry: NonExistentMessage +-- 36. Test pb_to_json escaping +SELECT pb_to_json(pb_insert('\x'::protobuf, ARRAY['CoverageMsg', 'str_arr', '0'], E'hello "world"\\\n\t\b\f\r'), 'CoverageMsg'); + pb_to_json +--------------------------------------------- + {"str_arr":["hello \"world\"\\\n\t\b\f\r"]} +(1 row) diff --git a/sql/pgproto_test.sql b/sql/pgproto_test.sql index 918d661..9ffdff2 100644 --- a/sql/pgproto_test.sql +++ b/sql/pgproto_test.sql @@ -342,3 +342,6 @@ SELECT pb_insert('\x'::protobuf, ARRAY['NonExistentMessage', 'f', '0'], '1.23'); + +-- 36. Test pb_to_json escaping +SELECT pb_to_json(pb_insert('\x'::protobuf, ARRAY['CoverageMsg', 'str_arr', '0'], E'hello "world"\\\n\t\b\f\r'), 'CoverageMsg'); diff --git a/src/json.c b/src/json.c index 3b903e3..f7ae20c 100644 --- a/src/json.c +++ b/src/json.c @@ -44,7 +44,25 @@ pb_to_json_inner(const char *ptr, const char *end, const char *msg_name, StringI pb_to_json_inner(ptr, ptr + len, lookup.type_name, buf); } else { appendStringInfoChar(buf, '"'); - appendBinaryStringInfo(buf, ptr, (int)len); + for (int i = 0; i < (int)len; i++) { + char c = ptr[i]; + switch (c) { + case '"': appendStringInfoString(buf, "\\\""); break; + case '\\': appendStringInfoString(buf, "\\\\"); break; + case '\b': appendStringInfoString(buf, "\\b"); break; + case '\f': appendStringInfoString(buf, "\\f"); break; + case '\n': appendStringInfoString(buf, "\\n"); break; + case '\r': appendStringInfoString(buf, "\\r"); break; + case '\t': appendStringInfoString(buf, "\\t"); break; + default: + if ((unsigned char)c < 0x20) { + appendStringInfo(buf, "\\u%04x", (unsigned char)c); + } else { + appendStringInfoChar(buf, c); + } + break; + } + } appendStringInfoChar(buf, '"'); } ptr += len;