代码可以从 下载
实现由OBJECT到JSON的程序
QString JSONPaser::toJson(const QVariantMap& data)
{
QStringList members;
for(QVariantMap::ConstIterator it = data.begin(); it != data.end(); ++it)
{
members.append(QString("\"%1\": %2").arg(it.key()).arg(parseElement(it.value())));
}
return "{" + members.join(", ") + "}";
}
QString JSONPaser::parseElement(const QVariant& value)
{
switch(value.type())
{
case QVariant::Bool:
return value.toBool() ? "true" : "false";
case QVariant::Map:
return toJson(value.toMap());
case QVariant::Int:
return QString::number(value.toInt());
case QVariant::LongLong:
return QString::number(value.toLongLong());
case QVariant::Double:
return QString::number(value.toDouble());
case QVariant::UInt:
return QString::number(value.toUInt());
case QVariant::ULongLong:
return QString::number(value.toULongLong());
case QVariant::List:
return parseList(value.toList());
case QVariant::String:
return QString("\"%1\"").arg(value.toString().replace("\"", "\\\"")).replace("\n", "\\n").replace("\t", "\\t");
case QVariant::Invalid:
return "null";
default:
return QString();
}
}
QString JSONPaser::parseList(const QVariantList& list)
{
QStringList parts;
Q_FOREACH(QVariant variant, list)
{
parts.append(parseElement(variant));
}
return "[" + parts.join(", ") + "]";
}
由JSON到OBJECT的程序QVariantMap JSONPaser::toObject(const QString& json) throw(ParseException)
{
// Store the start and end of the string
m_next = json.constBegin();
m_end = json.constEnd();
// A JSON Object is the top-level item in the parse tree
return parseObject();
}
QVariantMap JSONPaser::parseObject()
{
/*
* object
* {}
* { members }
*/
QVariantMap data;
consume('{');
if(peekNext() != '}')
data = parseMembers();
consume('}');
return data;
}
QVariantMap JSONPaser::parseMembers()
{
/*
* members
* pair
* pair , members
*/
QVariantMap data;
QPair
pair;
// loop instead of recursing
do
{
// Grab a pair
pair = parsePair();
// Store it in our data
data[pair.first] = pair.second;
}
while(tryConsume(',')); // Loop if we've got a list separator
return data;
}
QPair JSONPaser::parsePair()
{
/*
* pair
* string : value
*/
QString key = parseString();
consume(':');
QVariant value = parseValue();
return qMakePair(key, value);
}
QVariantList JSONPaser::parseArray()
{
/*
* array
* []
* [ elements ]
*/
QVariantList data;
consume('[');
if(peekNext() != ']')
data = parseElements();
consume(']');
return data;
}
QVariantList JSONPaser::parseElements()
{
/*
* elements
* value
* value , elements
*/
QVariantList data;
// loop instead of recursing
do
{
// Grab a value
data += parseValue();
}
while(tryConsume(',')); // repeat if we've got a list separator
return data;
}
QVariant JSONPaser::parseValue()
{
/*
* value
* string
* number
* object
* array
* bool
* null
*/
tryConsume(':');
// Lookahead to work out the type of value
switch(peekNext().toAscii())
{
case '"':
return parseString();
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return parseNumber();
case '{':
return parseObject();
case '[':
return parseArray();
case 't': // true
case 'f': // false
return parseBool();
case 'n': // null
return parseNull();
default:
FAIL(QObject::tr("string, number, object, array, bool, or null"));
}
}
QString JSONPaser::parseString()
{
/*
* string
* ""
* " chars "
*/
QString data;
// Starting quotation marks
consume('"');
// If it's a non-empty string, grab the contents
if(*m_next != '"')
data = parseChars();
// Ending quotation marks
consume('"');
return data;
}
QString JSONPaser::parseChars()
{
/*
* chars
* char
* char chars
*/
QString data;
// chars contains at least one char
data = parseChar();
// See if there's more...
Q_FOREVER
{
try { data += parseChar(); }
catch(ParseException e)
{
// If not, it doesn't matter - roll back
m_next = m_sym--;
break;
}
}
return data;
}
QChar JSONPaser::parseChar()
{
/*
* char
* any character except for ", \, or control characters
* \"
* \\
* \/
* \b
* \f
* \n
* \r
* \t
* \u four-hex-digits
*/
// Grab the next character, without skipping whitespace
consume(false);
// We're not allowed unescaped quotation marks
if(*m_sym == '"')
FAIL(QObject::tr("Any unicode character except for \" or JSON escape sequences"));
// But some escape sequences are allowed
if(*m_sym == '\\')
{
QString digits;
switch(consume().toAscii())
{
case '"':
return '"';
case '\\':
return '\\';
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'u':
// Unicode 4-digit hex
for(int i = 0; i < 4; ++i)
{
digits += parseHexDigit();
}
return QChar(digits.toInt(0, 16));
default:
FAIL("[\"\\/bfnrtu]");
}
}
return *m_sym;
}
QVariant JSONPaser::parseNumber()
{
/*
* number
* int
* int frac
* int exp
* int frac exp
*/
// Every JSON number starts with an int
QString data = parseInt();
// Lookahead for fractions and exponents
// Do we have a fraction?
if(*m_next == '.') data += parseFrac();
// Do we have an exponent?
if(*m_next == 'e' || *m_next == 'E') data += parseExp();
// Try several return types...
bool ok;
QVariant ret;
ret = data.toInt(&ok); if(ok) return ret;
ret = data.toLongLong(&ok); if(ok) return ret;
ret = data.toDouble(&ok); if(ok) return ret;
// If this point is reached, don't know how to convert the string
// to an integer.
Q_ASSERT(false);
return QVariant();
}
QString JSONPaser::parseInt()
{
/*
* int
* digit
* digit1-9 digits
* - digit
* - digit1-9 digits
*/
QString data;
// Match any negation mark
if(tryConsume('-'))
data = "-";
// Grab the first digit...
QChar firstDigit = parseDigit();
data += firstDigit;
// ...if it's not zero...
if(firstDigit != '0')
{
// ... try and add more digits.
try { data += parseDigits(); }
catch(ParseException)
{
// Catch, as more digits are entirely optional
// Roll back.
m_next = m_sym--;
}
}
return data;
}
QString JSONPaser::parseFrac()
{
/*
* frac
* . digits
*/
consume('.');
return QString(".%1").arg(parseDigits());
}
QString JSONPaser::parseExp()
{
/*
* exp
* e digits
*/
QString data;
data = parseE();
data += parseDigits();
return data;
}
QString JSONPaser::parseDigits()
{
/*
* digits
* digit
* digit digits
*/
QString data;
// Digits has at least one digit...
data += parseDigit();
// ... try and get some more
// Loop instead of recurse
Q_FOREVER
{
try { data += parseDigit(); }
catch(ParseException)
{
m_next = m_sym--; // roll back
break;
}
}
return data;
}
QString JSONPaser::parseE()
{
/*
* e
* e
* e+
* e-
* E
* E+
* E-
*/
// Try and grab an 'e' or 'E'
if(consume(false).toLower() == 'e')
{
// digits in follow[e]
if(m_next->isDigit())
return "e";
// Hopefully the next is a + or -
// grab another chracter...
consume(false);
// If it's not + or -, fail
if(*m_sym != '+' && *m_sym != '-')
FAIL("+ | -");
// Otherwise, return e[+-]
return QString("e%1").arg(*m_sym);
}
else
FAIL("e | E");
}
QChar JSONPaser::parseDigit()
{
/*
* digit
* [0-9]
*/
if(!consume(false).isDigit())
FAIL("[0-9]");
return *m_sym;
}
QChar JSONPaser::parseHexDigit()
{
/*
* hexdigit
* [0-9a-fA-F]
*/
QChar character = consume().toLower();
if(character.isDigit() || (character >= 'a' && character <= 'f'))
return character;
FAIL("[0-9a-fA-F]");
}
bool JSONPaser::parseBool()
{
/*
* bool
* true
* false
*/
switch(peekNext().toAscii())
{
case 't':
consume(QString("true"));
return true;
case 'f':
consume(QString("false"));
return false;
default:
consume(false);
FAIL("true | false");
}
}
QVariant JSONPaser::parseNull()
{
/*
* null
* null
*/
consume(QString("null"));
return QVariant();
}
QString JSONPaser::remaining()
{
QString data;
QString::ConstIterator it = m_sym;
while(it != m_end) data += *it++;
return data;
}
QChar JSONPaser::consume(bool skipWhitespace) throw(ParseException)
{
// Read a character...
do
{
if(m_next == m_end)
{
throw ParseException("EOF", "symbol", remaining());
}
m_sym = m_next++;
}
//...and loop while we get whitespace, if it's being skipped
while(skipWhitespace && m_sym->isSpace());
// Just for convenience...
return *m_sym;
}
void JSONPaser::consume(QChar wanted) throw(ParseException)
{
// Grab a char(ignoring whitespace), and if it's not what's
// expected, throw
if(consume() != wanted)
{
FAIL(wanted);
}
}
void JSONPaser::consume(char wanted) throw(ParseException)
{
// Convenience function for the above
consume(QChar(wanted));
}
void JSONPaser::consume(QString wanted) throw(ParseException)
{
// Keep track of where we start...
QString::ConstIterator it = m_sym;
// Grab wanted.length() characters, and compare them to the
// character in the appropriate position in the parameter
for(int i = 0; i < wanted.length(); ++i)
if(consume(false) != wanted[i])
{
// If it doesn't match, roll back, and throw a
// parse exception
m_sym = it;
m_next = ++it;
FAIL(wanted);
}
}
bool JSONPaser::tryConsume(QChar wanted) throw()
{
// Grab the next character
try
{
consume();
}
catch(ParseException)
{
// End-Of-String, rollback and return false
m_next = m_sym--;
return false;
}
// Check if it's what we want
if(*m_sym != wanted)
{
// nope, something else, rollback and return false
m_next = m_sym--;
return false;
}
return true;
}
QChar JSONPaser::peekNext(bool skipWhitespace) throw(ParseException)
{
QString::ConstIterator it = m_sym;
do
{
++it;
if(it == m_end)
{
FAIL("symbol");
}
}
while(skipWhitespace && it->isSpace());
return *it;
}