StelJsonParser.cpp   StelJsonParser.cpp 
skipping to change at line 21 skipping to change at line 21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/ */
#include "StelJsonParser.hpp" #include "StelJsonParser.hpp"
#include <QDebug> #include <QDebug>
#include <QBuffer> #include <QJsonDocument>
#include <QDateTime>
#include <stdexcept> #include <stdexcept>
#include <stdio.h>
class StelJsonParserInstance
{
public:
StelJsonParserInstance(QIODevice* ain)
: input(ain)
, nextChar()
, hasNextChar(false)
{
cur = buffer;
last = cur;
}
inline void skipJson();
inline bool tryReadChar(char c);
inline bool skipAndConsumeChar(char r);
QByteArray readString();
QVariant readOther();
QVariant parse();
private:
QIODevice* input;
char nextChar;
bool hasNextChar;
#define BUFSIZESTATIC 65536
char buffer[BUFSIZESTATIC];
char* last;
char* cur;
inline bool getNextFromBuffer(char *c)
{
if (cur==last)
{
if (cur==buffer-1) // End of file
return false;
last = buffer + input->read(buffer, BUFSIZESTATIC);
if (last==buffer)
{
cur = buffer-1;
last = cur;
return false;
}
cur = buffer;
}
*c = *cur;
++cur;
return true;
}
inline bool getChar(char* c)
{
if (hasNextChar)
{
hasNextChar=false;
*c = nextChar;
return true;
}
return getNextFromBuffer(c);
}
inline void ungetChar(char c)
{
nextChar=c;
hasNextChar=true;
}
inline void skipLine()
{
if (hasNextChar && nextChar=='\n')
{
hasNextChar=false;
return;
}
hasNextChar=false;
char c;
while (getNextFromBuffer(&c) && c!='\n')
{}
}
inline bool atEnd()
{
return cur==buffer-1;
}
};
void StelJsonParserInstance::skipJson()
{
// There is a weakness in this code -- it will cause any standalone
'/' to be absorbed.
char c;
while (getChar(&c))
{
switch (c)
{
case ' ':
case '\t':
case '\n':
case '\r':
break;
case '/':
{
if (!getChar(&c))
return;
if (c=='/')
skipLine();
else
{
// We have a problem, we rem
oved a '/'.. This should never happen with properly formatted JSON though
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
}
}
break;
default:
ungetChar(c);
return;
}
}
}
bool StelJsonParserInstance::tryReadChar(char c)
{
char r;
if (!getChar(&r))
return false;
if (r == c)
return true;
ungetChar(r);
return false;
}
bool StelJsonParserInstance::skipAndConsumeChar(char r)
{
char c;
while (getChar(&c))
{
switch (c)
{
case ' ':
case '\t':
case '\n':
case '\r':
break;
case '/':
{
if (!getChar(&c))
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
if (c=='/')
skipLine();
else
{
// We have a problem, we rem
oved a '/'.. This should never happen with properly formatted JSON though
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
}
}
break;
default:
if (r==c)
return true;
ungetChar(c);
return false;
}
}
return false;
}
// Read a string without the initial "
QByteArray StelJsonParserInstance::readString()
{
QByteArray name;
char c;
while (getChar(&c))
{
switch (c)
{
case '"':
return name;
break;
case '\\':
{
bool gotChar=getChar(&c);
if (!gotChar) {qWarning() << "cannot read fu
rther, error?"; continue;}
if (c=='b') c='\b';
if (c=='f') c='\f';
if (c=='n') c='\n';
if (c=='r') c='\r';
if (c=='t') c='\t';
if (c=='u') {qWarning() << "don't support \\
uxxxx char"; continue;}
}
break;
default:
name+=c;
break;
}
}
if (atEnd())
throw std::runtime_error(qPrintable(QString("End of file bef
ore end of string: "+name)));
throw std::runtime_error(qPrintable(QString("Read error before end o
f string: "+name)));
}
QVariant StelJsonParserInstance::readOther()
{
QByteArray str;
char c;
while (getChar(&c))
{
if (c==' ' || c==',' || c=='\n' || c=='\r' || c==']' || c=='
\t' || c=='}')
{
ungetChar(c);
break;
}
str+=c;
}
bool ok;
const int i = str.toInt(&ok);
if (ok)
return i;
const double d = str.toDouble(&ok);
if (ok)
return d;
if (str=="true")
return QVariant(true);
if (str=="false")
return QVariant(false);
if (str=="null")
return QVariant();
QDateTime dt = QDateTime::fromString(str, Qt::ISODate);
if (dt.isValid())
return QVariant(dt);
throw std::runtime_error(qPrintable(QString("Invalid JSON value: \""
)+str+"\""));
}
// Parse the given input stream
QVariant StelJsonParserInstance::parse()
{
skipJson();
char r;
if (!getChar(&r))
return QVariant();
switch (r)
{
case '{':
{
// We've got an object (a tuple)
QVariantMap map;
if (skipAndConsumeChar('}'))
return map;
for (;;)
{
if (!skipAndConsumeChar('\"'))
{
char cc=0;
if (getChar(&cc))
throw std::runtime_error(qPr
intable(QString("Expected '\"' at beginning of string, found: '%1' (ASCII %
2)").arg(cc).arg((int)(cc))));
}
const QByteArray& ar = readString();
const QString& key = QString::fromUtf8(ar.co
nstData(), ar.size());
if (!skipAndConsumeChar(':'))
throw std::runtime_error(qPrintable(
QString("Expected ':' after a member name: ")+key));
skipJson();
map.insert(key, parse());
if (!skipAndConsumeChar(','))
break;
}
if (!skipAndConsumeChar('}'))
throw std::runtime_error("Expected '}' to cl
ose an object");
return map;
}
case '[':
{
// We've got an array (a vector)
QVariantList list;
if (skipAndConsumeChar(']'))
return list;
for (;;)
{
list.append(parse());
if (!skipAndConsumeChar(','))
break;
}
if (!skipAndConsumeChar(']'))
throw std::runtime_error("Expected ']' to cl
ose an array");
return list;
}
case '\"':
{
// We've got a string
const QByteArray& ar = readString();
return QString::fromUtf8(ar.constData(), ar.size());
}
default:
{
ungetChar(r);
return readOther();
}
}
}
QHash<int, void (*)(const QVariant&, QIODevice*, int)> StelJsonParser::othe
rSerializer;
// Serialize the passed QVariant as JSON into the output QIODevice
void StelJsonParser::write(const QVariant& v, QIODevice* output, int indent Level) void StelJsonParser::write(const QVariant& v, QIODevice* output, int indent Level)
{ {
switch (v.type()) QByteArray json = write(v, indentLevel);
{ output->write(json);
case QVariant::Bool:
output->write(v.toBool()==true ? "true" : "false");
break;
case QVariant::Invalid:
output->write("null");
break;
case QVariant::ByteArray:
{
QByteArray s(v.toByteArray());
s.replace('\\', "\\\\");
s.replace('\"', "\\\"");
s.replace('\b', "\\b");
s.replace('\n', "\\n");
s.replace('\f', "\\f");
s.replace('\r', "\\r");
s.replace('\t', "\\t");
output->write("\"" + s + "\"");
break;
}
case QVariant::String:
{
QString s(v.toString());
s.replace('\\', "\\\\");
s.replace('\"', "\\\"");
s.replace('\b', "\\b");
s.replace('\n', "\\n");
s.replace('\f', "\\f");
s.replace('\r', "\\r");
s.replace('\t', "\\t");
output->write(QString("\"%1\"").arg(s).toUtf8());
break;
}
case QVariant::Int:
case QVariant::Double:
output->write(v.toString().toUtf8());
break;
case QVariant::DateTime:
{
output->write(v.toDateTime().toString(Qt::ISODate).t
oUtf8());
break;
}
case QVariant::List:
{
output->putChar('[');
const QVariantList& l = v.toList();
for (int i=0;i<l.size();++i)
{
// Break line if we start an JSON Object for
nice looking
if (l.at(i).type()==QVariant::Map)
output->putChar('\n');
write(l.at(i), output, indentLevel);
if (i!=l.size()-1)
output->write(", ");
}
output->putChar(']');
break;
}
case QVariant::Map:
{
const QByteArray prepend(indentLevel, '\t');
output->write(prepend);
output->write("{\n");
++indentLevel;
const QVariantMap& m = v.toMap();
int j =0;
for (QVariantMap::ConstIterator i=m.constBegin();i!=
m.constEnd();++i)
{
output->write(prepend);
output->write("\t\"");
output->write(i.key().toUtf8());
output->write("\": ");
// Break line if we start an JSON Object for
nice looking
if (i.value().type()==QVariant::Map)
output->putChar('\n');
write(i.value(), output, indentLevel);
if (++j!=m.size())
output->putChar(',');
output->putChar('\n');
}
output->write(prepend);
output->write("}");
--indentLevel;
break;
}
default:
QHash<int, void (*)(const QVariant&, QIODevice*, int
)>::ConstIterator iter = otherSerializer.find(v.userType());
if (iter!=otherSerializer.constEnd())
{
iter.value()(v, output, indentLevel);
}
else
output->write("null");
//qDebug() << v.type() << v.userType() << v.typeName
();
//qWarning() << "Cannot serialize QVariant of type "
<< v.typeName() << " in JSON";
break;
}
} }
QByteArray StelJsonParser::write(const QVariant& jsonObject, int indentLeve l) QByteArray StelJsonParser::write(const QVariant& jsonObject, int indentLeve l)
{ {
QByteArray ar; QJsonDocument doc = QJsonDocument::fromVariant(jsonObject);
QBuffer buf(&ar); return doc.toJson();
buf.open(QIODevice::WriteOnly);
StelJsonParser::write(jsonObject, &buf, indentLevel);
buf.close();
return ar;
} }
QVariant StelJsonParser::parse(QIODevice* input) QVariant StelJsonParser::parse(QIODevice* input)
{ {
StelJsonParserInstance parser(input); QByteArray data = input->readAll();
return parser.parse(); return parse(data);
} }
QVariant StelJsonParser::parse(const QByteArray& aar) QVariant StelJsonParser::parse(const QByteArray& aar)
{ {
QByteArray ar = aar; QJsonParseError error;
QBuffer buf(&ar); QJsonDocument doc = QJsonDocument::fromJson(aar, &error);
buf.open(QIODevice::ReadOnly); if (error.error != QJsonParseError::NoError)
QVariant v = StelJsonParser::parse(&buf);
buf.close();
return v;
}
JsonListIterator::JsonListIterator(QIODevice* input)
{
parser = new StelJsonParserInstance(input);
if (!parser->skipAndConsumeChar('['))
{
throw std::runtime_error("Expected '[' to start a list itera
tor");
}
ahasNext = !parser->skipAndConsumeChar(']');
}
JsonListIterator::~JsonListIterator()
{
Q_ASSERT(parser);
delete parser;
parser=NULL;
}
QVariant JsonListIterator::next()
{
QVariant ret = parser->parse();
ahasNext = parser->skipAndConsumeChar(',');
if (!ahasNext)
{ {
if (!parser->skipAndConsumeChar(']')) throw std::runtime_error(error.errorString().toLatin1().cons
throw std::runtime_error("Expected ']' to end a list tData());
iterator");
} }
return ret; return doc.toVariant();
} }
 End of changes. 9 change blocks. 
480 lines changed or deleted 13 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/