123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- /**
- * \file json.h
- */
- #include <QDebug>
-
- #include "json.h"
- static QString sanitizeString(QString str)
- {
- str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
- str.replace(QLatin1String("\""), QLatin1String("\\\""));
- str.replace(QLatin1String("\b"), QLatin1String("\\b"));
- str.replace(QLatin1String("\f"), QLatin1String("\\f"));
- str.replace(QLatin1String("\n"), QLatin1String("\\n"));
- str.replace(QLatin1String("\r"), QLatin1String("\\r"));
- str.replace(QLatin1String("\t"), QLatin1String("\\t"));
- return QString(QLatin1String("\"%1\"")).arg(str);
- }
- static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep)
- {
- QByteArray res;
- foreach(const QByteArray &i, list)
- {
- if(!res.isEmpty())
- {
- res += sep;
- }
- res += i;
- }
- return res;
- }
- /**
- * parse
- */
- QVariant Json::parse(const QString &json)
- {
- bool success = true;
- return Json::parse(json, success);
- }
- /**
- * parse
- */
- QVariant Json::parse(const QString &json, bool &success)
- {
- success = true;
- //Return an empty QVariant if the JSON data is either null or empty
- if(!json.isNull() || !json.isEmpty())
- {
- QString data = json;
- //We'll start from index 0
- int index = 0;
- //Parse the first value
- QVariant value = Json::parseValue(data, index, success);
- //Return the parsed value
- return value;
- }
- else
- {
- //Return the empty QVariant
- return QVariant();
- }
- }
- QByteArray Json::serialize(const QVariant &data)
- {
- bool success = true;
- return Json::serialize(data, success);
- }
- QByteArray Json::serialize(const QVariant &data, bool &success)
- {
- QByteArray str;
- success = true;
- if(!data.isValid()) // invalid or null?
- {
- str = "null";
- }
- else if(data.type() == QVariant::List) // variant is a list?
- {
- QList<QByteArray> values;
- const QVariantList list = data.toList();
- foreach(const QVariant& v, list)
- {
- QByteArray serializedValue = serialize(v);
- if(serializedValue.isNull())
- {
- success = false;
- break;
- }
- values << serializedValue;
- }
- str = "[ " + join( values, ", " ) + " ]";
- }
- else if(data.type() == QVariant::Map) // variant is a map?
- {
- const QVariantMap vmap = data.toMap();
- QMapIterator<QString, QVariant> it( vmap );
- str = "{ ";
- QList<QByteArray> pairs;
- while(it.hasNext())
- {
- it.next();
- QByteArray serializedValue = serialize(it.value());
- if(serializedValue.isNull())
- {
- success = false;
- break;
- }
- pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
- }
- str += join(pairs, ", ");
- str += " }";
- }
- else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array?
- {
- str = sanitizeString(data.toString()).toUtf8();
- }
- else if(data.type() == QVariant::Double) // double?
- {
- str = QByteArray::number(data.toDouble());
- if(!str.contains(".") && ! str.contains("e"))
- {
- str += ".0";
- }
- }
- else if (data.type() == QVariant::Bool) // boolean value?
- {
- str = data.toBool() ? "true" : "false";
- }
- else if (data.type() == QVariant::ULongLong) // large unsigned number?
- {
- str = QByteArray::number(data.value<qulonglong>());
- }
- else if ( data.canConvert<qlonglong>() ) // any signed number?
- {
- str = QByteArray::number(data.value<qlonglong>());
- }
- else if (data.canConvert<QString>()) // can value be converted to string?
- {
- // this will catch QDate, QDateTime, QUrl, ...
- str = sanitizeString(data.toString()).toUtf8();
- }
- else
- {
- success = false;
- }
- if (success)
- {
- return str;
- }
- else
- {
- return QByteArray();
- }
- }
- /**
- * parseValue
- */
- QVariant Json::parseValue(const QString &json, int &index, bool &success)
- {
- //Determine what kind of data we should parse by
- //checking out the upcoming token
- switch(Json::lookAhead(json, index))
- {
- case JsonTokenString:
- return Json::parseString(json, index, success);
- case JsonTokenNumber:
- return Json::parseNumber(json, index);
- case JsonTokenCurlyOpen:
- return Json::parseObject(json, index, success);
- case JsonTokenSquaredOpen:
- return Json::parseArray(json, index, success);
- case JsonTokenTrue:
- Json::nextToken(json, index);
- return QVariant(true);
- case JsonTokenFalse:
- Json::nextToken(json, index);
- return QVariant(false);
- case JsonTokenNull:
- Json::nextToken(json, index);
- return QVariant();
- case JsonTokenNone:
- break;
- }
- //If there were no tokens, flag the failure and return an empty QVariant
- success = false;
- return QVariant();
- }
- /**
- * parseObject
- */
- QVariant Json::parseObject(const QString &json, int &index, bool &success)
- {
- QVariantMap map;
- int token;
- //Get rid of the whitespace and increment index
- Json::nextToken(json, index);
- //Loop through all of the key/value pairs of the object
- bool done = false;
- while(!done)
- {
- //Get the upcoming token
- token = Json::lookAhead(json, index);
- if(token == JsonTokenNone)
- {
- success = false;
- return QVariantMap();
- }
- else if(token == JsonTokenComma)
- {
- Json::nextToken(json, index);
- }
- else if(token == JsonTokenCurlyClose)
- {
- Json::nextToken(json, index);
- return map;
- }
- else
- {
- //Parse the key/value pair's name
- QString name = Json::parseString(json, index, success).toString();
- if(!success)
- {
- return QVariantMap();
- }
- //Get the next token
- token = Json::nextToken(json, index);
- //If the next token is not a colon, flag the failure
- //return an empty QVariant
- if(token != JsonTokenColon)
- {
- success = false;
- return QVariant(QVariantMap());
- }
- //Parse the key/value pair's value
- QVariant value = Json::parseValue(json, index, success);
- if(!success)
- {
- return QVariantMap();
- }
- //Assign the value to the key in the map
- map[name] = value;
- }
- }
- //Return the map successfully
- return QVariant(map);
- }
- /**
- * parseArray
- */
- QVariant Json::parseArray(const QString &json, int &index, bool &success)
- {
- QVariantList list;
- Json::nextToken(json, index);
- bool done = false;
- while(!done)
- {
- int token = Json::lookAhead(json, index);
- if(token == JsonTokenNone)
- {
- success = false;
- return QVariantList();
- }
- else if(token == JsonTokenComma)
- {
- Json::nextToken(json, index);
- }
- else if(token == JsonTokenSquaredClose)
- {
- Json::nextToken(json, index);
- break;
- }
- else
- {
- QVariant value = Json::parseValue(json, index, success);
- if(!success)
- {
- return QVariantList();
- }
- list.push_back(value);
- }
- }
- return QVariant(list);
- }
- /**
- * parseString
- */
- QVariant Json::parseString(const QString &json, int &index, bool &success)
- {
- QString s;
- QChar c;
- Json::eatWhitespace(json, index);
- c = json[index++];
- bool complete = false;
- while(!complete)
- {
- if(index == json.size())
- {
- break;
- }
- c = json[index++];
- if(c == '\"')
- {
- complete = true;
- break;
- }
- else if(c == '\\')
- {
- if(index == json.size())
- {
- break;
- }
- c = json[index++];
- if(c == '\"')
- {
- s.append('\"');
- }
- else if(c == '\\')
- {
- s.append('\\');
- }
- else if(c == '/')
- {
- s.append('/');
- }
- else if(c == 'b')
- {
- s.append('\b');
- }
- else if(c == 'f')
- {
- s.append('\f');
- }
- else if(c == 'n')
- {
- s.append('\n');
- }
- else if(c == 'r')
- {
- s.append('\r');
- }
- else if(c == 't')
- {
- s.append('\t');
- }
- else if(c == 'u')
- {
- int remainingLength = json.size() - index;
- if(remainingLength >= 4)
- {
- QString unicodeStr = json.mid(index, 4);
- int symbol = unicodeStr.toInt(0, 16);
-
- s.append(QChar(symbol));
- index += 4;
- }
- else
- {
- break;
- }
- }
- }
- else
- {
- s.append(c);
- }
- }
- if(!complete)
- {
- success = false;
- return QVariant();
- }
- return QVariant(s);
- }
- /**
- * parseNumber
- */
- QVariant Json::parseNumber(const QString &json, int &index)
- {
- Json::eatWhitespace(json, index);
- int lastIndex = Json::lastIndexOfNumber(json, index);
- int charLength = (lastIndex - index) + 1;
- QString numberStr;
- numberStr = json.mid(index, charLength);
-
- index = lastIndex + 1;
- return QVariant(numberStr.toDouble(NULL));
- }
- /**
- * lastIndexOfNumber
- */
- int Json::lastIndexOfNumber(const QString &json, int index)
- {
- int lastIndex;
- for(lastIndex = index; lastIndex < json.size(); lastIndex++)
- {
- if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1)
- {
- break;
- }
- }
- return lastIndex -1;
- }
- /**
- * eatWhitespace
- */
- void Json::eatWhitespace(const QString &json, int &index)
- {
- for(; index < json.size(); index++)
- {
- if(QString(" \t\n\r").indexOf(json[index]) == -1)
- {
- break;
- }
- }
- }
- /**
- * lookAhead
- */
- int Json::lookAhead(const QString &json, int index)
- {
- int saveIndex = index;
- return Json::nextToken(json, saveIndex);
- }
- /**
- * nextToken
- */
- int Json::nextToken(const QString &json, int &index)
- {
- Json::eatWhitespace(json, index);
- if(index == json.size())
- {
- return JsonTokenNone;
- }
- QChar c = json[index];
- index++;
- switch(c.toAscii())
- {
- case '{': return JsonTokenCurlyOpen;
- case '}': return JsonTokenCurlyClose;
- case '[': return JsonTokenSquaredOpen;
- case ']': return JsonTokenSquaredClose;
- case ',': return JsonTokenComma;
- case '"': return JsonTokenString;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case '-': return JsonTokenNumber;
- case ':': return JsonTokenColon;
- }
- index--;
- int remainingLength = json.size() - index;
- //True
- if(remainingLength >= 4)
- {
- if (json[index] == 't' && json[index + 1] == 'r' &&
- json[index + 2] == 'u' && json[index + 3] == 'e')
- {
- index += 4;
- return JsonTokenTrue;
- }
- }
- //False
- if (remainingLength >= 5)
- {
- if (json[index] == 'f' && json[index + 1] == 'a' &&
- json[index + 2] == 'l' && json[index + 3] == 's' &&
- json[index + 4] == 'e')
- {
- index += 5;
- return JsonTokenFalse;
- }
- }
- //Null
- if (remainingLength >= 4)
- {
- if (json[index] == 'n' && json[index + 1] == 'u' &&
- json[index + 2] == 'l' && json[index + 3] == 'l')
- {
- index += 4;
- return JsonTokenNull;
- }
- }
- return JsonTokenNone;
- }
|