184 lines
5.9 KiB
C++
184 lines
5.9 KiB
C++
|
#include "qjson.h"
|
||
|
|
||
|
inline QChar readOne(QTextStream &in) {
|
||
|
QChar ch;
|
||
|
in >> ch;
|
||
|
return ch;
|
||
|
}
|
||
|
JValue JParser::readValue() {
|
||
|
if(ch=='{') {
|
||
|
JObj obj;
|
||
|
while(true) {
|
||
|
do skipSpace(); //ch有三种可能
|
||
|
while(ch==',');
|
||
|
if(ch=='}') return obj;
|
||
|
QString key;
|
||
|
if(ch == '"') key = readStr();
|
||
|
else if(ch!='n' || in.read(3)!="ull") throw QString("Unexpected char ")+ch+" (code "+ch+"): was expecting double-quote to start field name or null";
|
||
|
skipSpace();
|
||
|
if(ch != ':') throw QString("Unexpected char ")+ch+" (code "+ch+"): was expecting a colon to separate field name and value";
|
||
|
skipSpace();
|
||
|
obj.insert(key, readValue());
|
||
|
skipSpace(); //ch有两种可能
|
||
|
if(ch=='}') return obj;
|
||
|
if(ch!=',') throw QString("Unexpected char ")+ch+"' (code "+ch+"): was expecting } to end Object or comma to separate Object entries";
|
||
|
}
|
||
|
}
|
||
|
if(ch=='[') {
|
||
|
JArray list;
|
||
|
while(true) {
|
||
|
do skipSpace(); //ch有三种可能
|
||
|
while(ch==',');
|
||
|
if(ch==']') return list;
|
||
|
list->push_back(readValue());
|
||
|
skipSpace(); //ch有两种可能
|
||
|
if(ch==']') return list;
|
||
|
if(ch!=',') throw QString("Unexpected char ")+ch+" (code "+ch+"): was expecting ] to end Array or comma to separate Array entries";
|
||
|
}
|
||
|
}
|
||
|
if(ch=='"') return readStr();
|
||
|
if((ch>='0' && ch<='9') || ch=='-') {
|
||
|
QString buf;
|
||
|
buf += ch;
|
||
|
bool isInt = true;
|
||
|
while(0 != (ch = readOne(in))) {
|
||
|
if((ch>'*' && ch<':' && ch!=',' && ch!='/') || ch=='e' || ch=='E') {
|
||
|
buf.append(ch);
|
||
|
if(isInt && ch=='.') isInt = false;
|
||
|
} else {
|
||
|
bk = ch;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
bool ok;
|
||
|
if(isInt) {
|
||
|
auto num = buf.toLongLong(&ok);
|
||
|
if(! ok) throw "Illegal number: "+buf;
|
||
|
return num;
|
||
|
} else {
|
||
|
auto num = buf.toDouble(&ok);
|
||
|
if(! ok) throw "Illegal number: "+buf;
|
||
|
return num;
|
||
|
}
|
||
|
}
|
||
|
if(ch=='n') {
|
||
|
if(in.read(3)=="ull") return JValue();
|
||
|
else throw "Unexpected char n: expected null";
|
||
|
}
|
||
|
if(ch=='t') {
|
||
|
if(in.read(3)=="rue") return true;
|
||
|
else throw "Unexpected char t: expected true";
|
||
|
}
|
||
|
if(ch=='f') {
|
||
|
if(in.read(4)=="alse") return false;
|
||
|
else throw "Unexpected char f: expected false";
|
||
|
}
|
||
|
throw QString("Unexpected char ")+ch+" (code "+ch+"): expected {}, [], \"string\", number, null, true or false";
|
||
|
}
|
||
|
QString JParser::readStr() {
|
||
|
QString buf;
|
||
|
while((ch = readOne(in)) != '"') {
|
||
|
if(ch==0) throw "Unexpected end-of-input: was expecting closing quote for string";
|
||
|
if(ch=='\\') {
|
||
|
in>>ch;
|
||
|
if(ch==0) throw "Unexpected end-of-input in char escape sequence";
|
||
|
if(ch=='"' || ch=='\\' || ch=='/') buf.append(ch);
|
||
|
else if(ch=='n') buf.append('\n');
|
||
|
else if(ch=='r') buf.append('\r');
|
||
|
else if(ch=='t') buf.append('\t');
|
||
|
else if(ch=='f') buf.append('\f');
|
||
|
else if(ch=='b') buf.append('\b');
|
||
|
else if(ch=='u') {
|
||
|
auto hex = in.read(4);
|
||
|
if(hex.size()<4) throw "Unexpected end-of-input in char escape sequence";
|
||
|
bool ok;
|
||
|
buf.append(hex.toUShort(&ok, 16));
|
||
|
if(! ok) throw "Illegal hex-digits in char escape sequence: \\u"+hex;
|
||
|
} else throw QString("Unrecognized char-escape ")+ch+" (code "+ch+")";
|
||
|
} else buf.append(ch);
|
||
|
}
|
||
|
return buf;
|
||
|
}
|
||
|
void JParser::skipSpace() {
|
||
|
if(bk.unicode()) {
|
||
|
bk = QChar::Null;
|
||
|
if(! ch.isSpace()) return;
|
||
|
}
|
||
|
in.skipWhiteSpace();
|
||
|
in >> ch;
|
||
|
if(ch==0) throw "Unexpected end-of-input";
|
||
|
}
|
||
|
|
||
|
void JOut::write(const JValue &value) {
|
||
|
if(value.type==JValue::Null) out << "null";
|
||
|
else if(value.type==JValue::Str) writeStr(value.toStr());
|
||
|
else if(value.type==JValue::Obj) writeMap(value.toObj());
|
||
|
else if(value.type==JValue::Array) writeList(value.toArray());
|
||
|
else out << value.toStr();
|
||
|
out.flush();
|
||
|
}
|
||
|
void JOut::writeStr(const QString &str) {
|
||
|
out << '"';
|
||
|
QChar ch;
|
||
|
for(int i=0; i<str.length(); i++) {
|
||
|
ch = str[i];
|
||
|
if(ch=='"'||ch=='\\') out<<'\\'<<ch;
|
||
|
else if(ch < ' ') {
|
||
|
if(ch=='\n') out<<"\\n";
|
||
|
else if(ch=='\r') out<<"\\r";
|
||
|
else if(ch=='\t') out<<"\\t";
|
||
|
else if(ch=='\f') out<<"\\f";
|
||
|
else if(ch=='\b') out<<"\\b";
|
||
|
else {
|
||
|
out<<"\\u00";
|
||
|
auto aa = QString::number(ch.unicode(), 16);
|
||
|
if(aa.size()==1) out<<'0';
|
||
|
out<<aa;
|
||
|
}
|
||
|
} else out << ch;
|
||
|
}
|
||
|
out << '"';
|
||
|
}
|
||
|
void JOut::writeMap(const JObj &map) {
|
||
|
out << '{';
|
||
|
if(! map.empty()) {
|
||
|
if(indent.size()) {
|
||
|
out << '\n';
|
||
|
cnt++;
|
||
|
for(int c=0; c<cnt; c++) out << indent;
|
||
|
}
|
||
|
bool addSep = false;
|
||
|
for(const auto &pair : map) {
|
||
|
if(addSep) {
|
||
|
out << ',';
|
||
|
if(indent.size()) {
|
||
|
out << '\n';
|
||
|
for(int c=0; c<cnt; c++) out << indent;
|
||
|
}
|
||
|
}
|
||
|
else addSep = true;
|
||
|
out << '"' << pair.first << "\":";
|
||
|
if(indent.size()) out << ' ';
|
||
|
write(pair.second);
|
||
|
}
|
||
|
if(indent.size()) {
|
||
|
out << '\n';
|
||
|
cnt--;
|
||
|
for(int c=0; c<cnt; c++) out << indent;
|
||
|
}
|
||
|
}
|
||
|
out << '}';
|
||
|
}
|
||
|
void JOut::writeList(const JArray &vals) {
|
||
|
out << '[';
|
||
|
auto iter = vals.begin();
|
||
|
auto end = vals.end();
|
||
|
if(iter!=end) write(*iter++);
|
||
|
while(iter!=end) {
|
||
|
out << ',';
|
||
|
if(indent.size()) out << ' ';
|
||
|
write(*iter++);
|
||
|
}
|
||
|
out << ']';
|
||
|
}
|