qt/ledset/gutil/qjson.cpp
2023-07-28 14:48:41 +08:00

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 << ']';
}