diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2011-12-07 11:11:49 +0100 |
---|---|---|
committer | Simons Kevin <kevin.simons@nokia.com> | 2011-12-07 13:16:06 +0100 |
commit | 7f2c87f97beddc28cc6357d89f19f63c74d12423 (patch) | |
tree | 3d5d4bb2f3e2002a00a3860b1c0ed02dbe4c8a31 | |
parent | 4cb4e4137bacd15c97f1107a99590ff871381e49 (diff) |
Move the code for writing json into it's own class
QJsonWriter is an internal class to convert Json
documents to their text based representation.
Also fix QJsonDocument::to/fromVariant.
Change-Id: I20604b5f0bd75e5e4931cf9a0d391c967ea4d928
Reviewed-by: Simons Kevin <kevin.simons@nokia.com>
-rw-r--r-- | src/qjson_p.h | 2 | ||||
-rw-r--r-- | src/qjsondocument.cpp | 51 | ||||
-rw-r--r-- | src/qjsondocument.h | 2 | ||||
-rw-r--r-- | src/qjsonobject.cpp | 218 | ||||
-rw-r--r-- | src/qjsonwriter.cpp | 234 | ||||
-rw-r--r-- | src/qjsonwriter_p.h | 13 | ||||
-rw-r--r-- | src/src.pro | 6 | ||||
-rw-r--r-- | tests/auto/tst_qtjson.cpp | 17 |
8 files changed, 307 insertions, 236 deletions
diff --git a/src/qjson_p.h b/src/qjson_p.h index 1fb16ef..491a409 100644 --- a/src/qjson_p.h +++ b/src/qjson_p.h @@ -3,6 +3,8 @@ #include <qjsonglobal.h> #include <qjsonobject.h> +#include <qjsonvalue.h> +#include <qjsonarray.h> #include <qbasicatomic.h> #include <qstring.h> diff --git a/src/qjsondocument.cpp b/src/qjsondocument.cpp index 440e102..26432d4 100644 --- a/src/qjsondocument.cpp +++ b/src/qjsondocument.cpp @@ -3,6 +3,7 @@ #include <qjsonvalue.h> #include <qjsonarray.h> #include <qjson_p.h> +#include <qjsonwriter_p.h> #include <qstringlist.h> using namespace QtJson; @@ -65,33 +66,49 @@ JsonDocument JsonDocument::fromData(const QByteArray &data) return JsonDocument(d); } -JsonDocument JsonDocument::fromVariant(const QVariant &map) +JsonDocument JsonDocument::fromVariant(const QVariant &v) { -// // ### this is implemented the trivial way, not the most efficient way - -// JsonDocument doc; -// for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) -// object.insert(it.key(), JsonValue::fromVariant(it.value())); -// return object; + JsonDocument doc; + if (v.type() == QVariant::Map) { + doc.setObject(JsonObject::fromVariantMap(v.toMap())); + } else if (v.type() == QVariant::List) { + doc.setArray(JsonArray::fromVariantList(v.toList())); + } else if (v.type() == QVariant::StringList) { + doc.setArray(JsonArray::fromStringList(v.toStringList())); + } + return doc; } QVariant JsonDocument::toVariant() const { -// QVariantMap map; -// for (uint i = 0; i < o->length; ++i) { -// Entry *e = o->entryAt(i); -// map.insert(e->key(), JsonValue(d, e->value()).toVariant()); -// } -// return map; + ValueType type = d ? (ValueType)d->_header->type : NullValue; + switch (type) { + case ArrayValue: + return JsonArray(d, static_cast<Array *>(&d->_header->root)).toVariantList(); + break; + case ObjectValue: + return JsonObject(d, static_cast<Object *>(&d->_header->root)).toVariantMap(); + break; + default: + break; + } + return QVariant(); } QByteArray JsonDocument::toJson() const { QByteArray json; -// json.reserve(o ? o->size/2 : 0); -// json += "{\n"; -// objectContentToJson(o, json, 1); -// json += "}\n"; + ValueType type = d ? (ValueType)d->_header->type : NullValue; + switch (type) { + case ArrayValue: + QJsonWriter::arrayToJson(static_cast<Array *>(&d->_header->root), json, 0); + break; + case ObjectValue: + QJsonWriter::objectToJson(static_cast<Object *>(&d->_header->root), json, 0); + break; + default: + break; + } return json; } diff --git a/src/qjsondocument.h b/src/qjsondocument.h index 55d2511..2bb3906 100644 --- a/src/qjsondocument.h +++ b/src/qjsondocument.h @@ -16,7 +16,7 @@ public: JsonDocument &operator =(const JsonDocument &other); static JsonDocument fromData(const QByteArray &data); - static JsonDocument fromVariant(const QVariant &map); + static JsonDocument fromVariant(const QVariant &v); QVariant toVariant() const; QByteArray toJson() const; diff --git a/src/qjsonobject.cpp b/src/qjsonobject.cpp index b624ebd..9b19845 100644 --- a/src/qjsonobject.cpp +++ b/src/qjsonobject.cpp @@ -2,6 +2,7 @@ #include <qjsonvalue.h> #include <qjsonarray.h> #include <qjson_p.h> +#include <qjsonwriter_p.h> #include <qstringlist.h> using namespace QtJson; @@ -81,225 +82,10 @@ QVariantMap JsonObject::toVariantMap() const return map; } -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; - switch (type) { - case BooleanValue: - json += v->val ? "true" : "false"; - break; - case NumberValue: - json += QByteArray::number(v->toNumber()); - break; - case StringValue: - json += '"'; - json += escapedString(v->shallowString()); - json += '"'; - break; - case ArrayValue: - json += "[\n"; - arrayContentToJson(v->array(), json, indent + 1); - json += QByteArray(4*indent, ' '); - json += "]"; - break; - case ObjectValue: - json += "{\n"; - objectContentToJson(v->object(), json, indent + 1); - json += QByteArray(4*indent, ' '); - json += "}"; - break; - case NullValue: - default: - json += "null"; - } -} - -static void arrayContentToJson(const Array *a, QByteArray &json, int indent) -{ - if (!a || !a->length) - return; - - QByteArray indentString(4*indent, ' '); - - uint i = 0; - while (1) { - json += indentString; - valueToJson(a->at(i), json, indent); - - if (++i == a->length) { - json += '\n'; - break; - } - - json += ",\n"; - } -} - - -static void objectContentToJson(const Object *o, QByteArray &json, int indent) -{ - if (!o || !o->length) - return; - - QByteArray indentString(4*indent, ' '); - - uint i = 0; - while (1) { - Entry *e = o->entryAt(i); - json += indentString; - json += '"'; - json += escapedString(e->shallowKey()); - json += "\": "; - const Value *v = e->value(); - valueToJson(v, json, indent); - - if (++i == o->length) { - json += '\n'; - break; - } - - json += ",\n"; - } -} - QByteArray JsonObject::toJson() const { QByteArray json; - json.reserve(o ? o->size/2 : 0); - json += "{\n"; - objectContentToJson(o, json, 1); - json += "}\n"; + QJsonWriter::objectToJson(o, json, 0); return json; } diff --git a/src/qjsonwriter.cpp b/src/qjsonwriter.cpp new file mode 100644 index 0000000..133403d --- /dev/null +++ b/src/qjsonwriter.cpp @@ -0,0 +1,234 @@ +#include <qjsonwriter_p.h> +#include <qjson_p.h> + +using namespace QtJson; + +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; + switch (type) { + case BooleanValue: + json += v->val ? "true" : "false"; + break; + case NumberValue: + json += QByteArray::number(v->toNumber()); + break; + case StringValue: + json += '"'; + json += escapedString(v->shallowString()); + json += '"'; + break; + case ArrayValue: + json += "[\n"; + arrayContentToJson(v->array(), json, indent + 1); + json += QByteArray(4*indent, ' '); + json += "]"; + break; + case ObjectValue: + json += "{\n"; + objectContentToJson(v->object(), json, indent + 1); + json += QByteArray(4*indent, ' '); + json += "}"; + break; + case NullValue: + default: + json += "null"; + } +} + +static void arrayContentToJson(const Array *a, QByteArray &json, int indent) +{ + if (!a || !a->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + json += indentString; + valueToJson(a->at(i), json, indent); + + if (++i == a->length) { + json += '\n'; + break; + } + + json += ",\n"; + } +} + + +static void objectContentToJson(const Object *o, QByteArray &json, int indent) +{ + if (!o || !o->length) + return; + + QByteArray indentString(4*indent, ' '); + + uint i = 0; + while (1) { + Entry *e = o->entryAt(i); + json += indentString; + json += '"'; + json += escapedString(e->shallowKey()); + json += "\": "; + const Value *v = e->value(); + valueToJson(v, json, indent); + + if (++i == o->length) { + json += '\n'; + break; + } + + json += ",\n"; + } +} + +void QJsonWriter::objectToJson(const Object *o, QByteArray &json, int indent) +{ + json.reserve(json.size() + (o ? o->size : 16)); + json += "{\n"; + objectContentToJson(o, json, indent + 1); + json += QByteArray(4*indent, ' '); + json += "}\n"; +} + +void QJsonWriter::arrayToJson(const Array *a, QByteArray &json, int indent) +{ + json.reserve(json.size() + (a ? a->size : 16)); + json += "[\n"; + arrayContentToJson(a, json, indent + 1); + json += QByteArray(4*indent, ' '); + json += "]\n"; +} diff --git a/src/qjsonwriter_p.h b/src/qjsonwriter_p.h new file mode 100644 index 0000000..8eedb7b --- /dev/null +++ b/src/qjsonwriter_p.h @@ -0,0 +1,13 @@ +#include <qjsonglobal.h> + +namespace QtJson +{ + +class QJsonWriter +{ +public: + static void objectToJson(const Object *o, QByteArray &json, int indent); + static void arrayToJson(const Array *a, QByteArray &json, int indent); +}; + +} diff --git a/src/src.pro b/src/src.pro index a9a688c..ed34e65 100644 --- a/src/src.pro +++ b/src/src.pro @@ -11,13 +11,15 @@ HEADERS += \ qjsonobject.h \ qjsonglobal.h \ qjsonvalue.h \ - qjsonarray.h + qjsonarray.h \ + qjsonwriter_p.h SOURCES += \ qjsondocument.cpp \ qjsonobject.cpp \ qjsonarray.cpp \ - qjsonvalue.cpp + qjsonvalue.cpp \ + qjsonwriter.cpp diff --git a/tests/auto/tst_qtjson.cpp b/tests/auto/tst_qtjson.cpp index 9fa1258..3e6cf19 100644 --- a/tests/auto/tst_qtjson.cpp +++ b/tests/auto/tst_qtjson.cpp @@ -562,6 +562,23 @@ void TestQtJson::toJson() " ]\n" "}\n"; QCOMPARE(json, expected); + + JsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); + + doc.setArray(array); + json = doc.toJson(); + expected = + "[\n" + " true,\n" + " 999,\n" + " \"string\",\n" + " null,\n" + " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" + "]\n"; + QCOMPARE(json, expected); } |