summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2011-12-05 16:21:55 +0100
committerSimons Kevin <kevin.simons@nokia.com>2011-12-06 06:14:17 +0100
commitda1bb989f409431181fc5e7b58fde2af4986869f (patch)
tree26756ca88b129e6402bfdb2c90cc1585cfc08c9a
parent121b80db58a421ffcfda751babc0909bab42a422 (diff)
Properly escape strings when converting to text
Properly escape character sequences that need to be escaped in JSON. Change-Id: Ia2e3c53447df5b82330701232c868049b108eac6 Reviewed-by: Simons Kevin <kevin.simons@nokia.com>
-rw-r--r--src/qbinaryjsonobject.cpp132
-rw-r--r--tests/auto/tst_qtbinaryjson.cpp8
2 files changed, 135 insertions, 5 deletions
diff --git a/src/qbinaryjsonobject.cpp b/src/qbinaryjsonobject.cpp
index 7086517..4243116 100644
--- a/src/qbinaryjsonobject.cpp
+++ b/src/qbinaryjsonobject.cpp
@@ -87,6 +87,134 @@ QVariantMap JsonObject::toVariantMap() const
static void objectContentToJson(const Object *o, QByteArray &json, int indent);
static void arrayContentToJson(const Array *a, QByteArray &json, int indent);
+// some code from qutfcodec.cpp, inlined here for performance reasons
+// to allow fast escaping of strings
+static inline bool isUnicodeNonCharacter(uint ucs4)
+{
+ // Unicode has a couple of "non-characters" that one can use internally,
+ // but are not allowed to be used for text interchange.
+ //
+ // Those are the last two entries each Unicode Plane (U+FFFE, U+FFFF,
+ // U+1FFFE, U+1FFFF, etc.) as well as the entries between U+FDD0 and
+ // U+FDEF (inclusive)
+
+ return (ucs4 & 0xfffe) == 0xfffe
+ || (ucs4 - 0xfdd0U) < 16;
+}
+
+static inline uchar hexdig(uint u)
+{
+ return (u < 0xa ? '0' + u : 'a' + u - 0xa);
+}
+
+static QByteArray escapedString(const QString &s)
+{
+ const uchar replacement = '?';
+ QByteArray ba(s.length(), Qt::Uninitialized);
+
+ uchar *cursor = (uchar *)ba.data();
+ const uchar *ba_end = cursor + ba.length();
+
+ const QChar *ch = s.constData();
+ const QChar *end = ch + s.length();
+
+ int surrogate_high = -1;
+
+ while (ch < end) {
+ if (cursor >= ba_end - 6) {
+ // ensure we have enough space
+ int pos = cursor - (uchar *)ba.constData();
+ ba.resize(ba.size()*2);
+ cursor = (uchar *)ba.data() + pos;
+ ba_end = (const uchar *)ba.constData() + ba.length();
+ }
+
+ uint u = ch->unicode();
+ if (surrogate_high >= 0) {
+ if (ch->isLowSurrogate()) {
+ u = QChar::surrogateToUcs4(surrogate_high, u);
+ surrogate_high = -1;
+ } else {
+ // high surrogate without low
+ *cursor = replacement;
+ ++ch;
+ surrogate_high = -1;
+ continue;
+ }
+ } else if (ch->isLowSurrogate()) {
+ // low surrogate without high
+ *cursor = replacement;
+ ++ch;
+ continue;
+ } else if (ch->isHighSurrogate()) {
+ surrogate_high = u;
+ ++ch;
+ continue;
+ }
+
+ if (u < 0x80) {
+ if (u < 0x20 || u == 0x22 || u == 0x5c) {
+ *cursor++ = '\\';
+ switch (u) {
+ case 0x22:
+ *cursor++ = '"';
+ break;
+ case 0x5c:
+ *cursor++ = '\\';
+ break;
+ case 0x8:
+ *cursor++ = 'b';
+ break;
+ case 0xc:
+ *cursor++ = 'f';
+ break;
+ case 0xa:
+ *cursor++ = 'n';
+ break;
+ case 0xd:
+ *cursor++ = 'r';
+ break;
+ case 0x9:
+ *cursor++ = 't';
+ break;
+ default:
+ *cursor++ = 'u';
+ *cursor++ = '0';
+ *cursor++ = '0';
+ *cursor++ = hexdig(u>>4);
+ *cursor++ = hexdig(u & 0xf);
+ }
+ } else {
+ *cursor++ = (uchar)u;
+ }
+ } else {
+ if (u < 0x0800) {
+ *cursor++ = 0xc0 | ((uchar) (u >> 6));
+ } else {
+ // is it one of the Unicode non-characters?
+ if (isUnicodeNonCharacter(u)) {
+ *cursor++ = replacement;
+ ++ch;
+ continue;
+ }
+
+ if (u > 0xffff) {
+ *cursor++ = 0xf0 | ((uchar) (u >> 18));
+ *cursor++ = 0x80 | (((uchar) (u >> 12)) & 0x3f);
+ } else {
+ *cursor++ = 0xe0 | (((uchar) (u >> 12)) & 0x3f);
+ }
+ *cursor++ = 0x80 | (((uchar) (u >> 6)) & 0x3f);
+ }
+ *cursor++ = 0x80 | ((uchar) (u&0x3f));
+ }
+ ++ch;
+ }
+
+ ba.resize(cursor - (const uchar *)ba.constData());
+ return ba;
+}
+
static void valueToJson(const Value *v, QByteArray &json, int indent)
{
ValueType type = v ? (ValueType)v->type : NullValue;
@@ -99,7 +227,7 @@ static void valueToJson(const Value *v, QByteArray &json, int indent)
break;
case StringValue:
json += '"';
- json += v->shallowString().toUtf8();
+ json += escapedString(v->shallowString());
json += '"';
break;
case ArrayValue:
@@ -154,7 +282,7 @@ static void objectContentToJson(const Object *o, QByteArray &json, int indent)
Entry *e = o->entryAt(i);
json += indentString;
json += '"';
- json += e->shallowKey().toUtf8();
+ json += escapedString(e->shallowKey());
json += "\": ";
const Value *v = e->value();
valueToJson(v, json, indent);
diff --git a/tests/auto/tst_qtbinaryjson.cpp b/tests/auto/tst_qtbinaryjson.cpp
index 2b3c042..9063025 100644
--- a/tests/auto/tst_qtbinaryjson.cpp
+++ b/tests/auto/tst_qtbinaryjson.cpp
@@ -477,26 +477,28 @@ void TestQtBinaryJson::toVariantMap()
void TestQtBinaryJson::toJson()
{
JsonObject object;
- object.insert("Key", QString("Value"));
+ object.insert("\\Key\n", QString("Value"));
object.insert("null", JsonValue());
JsonArray array;
array.append(true);
array.append(999.);
array.append(QLatin1String("string"));
array.append(JsonValue());
+ array.append(QLatin1String("\\\a\n\r\b\tabcABC\""));
object.insert("Array", array);
QByteArray json = object.toJson();
QByteArray expected =
"{\n"
- " \"Key\": \"Value\",\n"
+ " \"\\\\Key\\n\": \"Value\",\n"
" \"null\": null,\n"
" \"Array\": [\n"
" true,\n"
" 999,\n"
" \"string\",\n"
- " null\n"
+ " null,\n"
+ " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n"
" ]\n"
"}\n";
QCOMPARE(json, expected);