2023-07-21 17:40:49 +08:00
|
|
|
#ifndef QJSON_H
|
|
|
|
#define QJSON_H
|
|
|
|
|
2023-09-19 11:49:20 +08:00
|
|
|
#include "cpp.h"
|
2023-07-21 17:40:49 +08:00
|
|
|
#include "QtCore/qhashfunctions.h"
|
|
|
|
#include <QTextStream>
|
|
|
|
|
|
|
|
class JValue;
|
|
|
|
using JObj = LinkedMap<QString, JValue>;
|
|
|
|
using JArray = Vector<JValue>;
|
|
|
|
|
|
|
|
class JValue {
|
|
|
|
public:
|
2023-09-19 11:49:20 +08:00
|
|
|
long long data = 0;
|
2023-07-21 17:40:49 +08:00
|
|
|
enum Type {
|
2023-09-19 11:49:20 +08:00
|
|
|
Null, Long, Ulong, Double, Bool, Obj, Array, Str
|
2023-07-21 17:40:49 +08:00
|
|
|
};
|
2023-09-19 11:49:20 +08:00
|
|
|
Type type = Null;
|
2023-07-21 17:40:49 +08:00
|
|
|
|
|
|
|
JValue(Type = Null) {}
|
2023-09-19 11:49:20 +08:00
|
|
|
JValue(bool b) : type(Bool) {data = b;}
|
|
|
|
JValue(int n) : type(Long) {data = n;}
|
|
|
|
JValue(qint64 n) : type(Long) {data = n;}
|
|
|
|
JValue(quint64 n) : type(Ulong) {*(quint64*) &data = n;}
|
|
|
|
JValue(double d) : type(Double) {*(double*) &data = d;}
|
|
|
|
JValue(const JObj &o) : type(Obj) {new (&data) JObj(o);}
|
|
|
|
JValue(const JArray &a) : type(Array) {new (&data) JArray(a);}
|
|
|
|
JValue(JObj &&o) : type(Obj) {new (&data) JObj(std::move(o));}
|
|
|
|
JValue(JArray &&a) : type(Array) {new (&data) JArray(std::move(a));}
|
|
|
|
JValue(const QString &s) : type(Str) {*(SharedData<QString>**) &data = new SharedData<QString>{1, s};}
|
|
|
|
JValue(QString &&s) : type(Str) {*(SharedData<QString>**) &data = new SharedData<QString>{1, std::move(s)};}
|
2023-07-21 17:40:49 +08:00
|
|
|
JValue(const char *s) : JValue(QString::fromUtf8(s)) {}
|
|
|
|
~JValue() {
|
|
|
|
if(type < Obj) return;
|
2023-09-19 11:49:20 +08:00
|
|
|
else if(type==Obj) ((JObj*) &data)->~JObj();
|
|
|
|
else if(type==Array) ((JArray*) &data)->~JArray();
|
2023-07-21 17:40:49 +08:00
|
|
|
else if(type==Str) {
|
2023-09-19 11:49:20 +08:00
|
|
|
auto ptr = *(SharedData<QString>**) &data;
|
2023-07-21 17:40:49 +08:00
|
|
|
if(ptr->cnt > 1) ptr->cnt--;
|
|
|
|
else delete ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-19 11:49:20 +08:00
|
|
|
JValue(const JValue &other) {
|
|
|
|
type = other.type;
|
|
|
|
if(type==Obj) new (&data) JObj(*(JObj*) &other.data);
|
|
|
|
else if(type==Array) new (&data) JArray(*(JArray*) &other.data);
|
|
|
|
else {
|
|
|
|
data = other.data;
|
|
|
|
if(type==Str) (*(SharedData<QString>**) &data)->cnt++;
|
|
|
|
}
|
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
JValue &operator=(const JValue &other) {
|
|
|
|
this->~JValue();
|
|
|
|
new (this) JValue(other);
|
|
|
|
return *this;
|
|
|
|
}
|
2023-09-19 11:49:20 +08:00
|
|
|
JValue(JValue &&other) noexcept : data(other.data), type(other.type) {
|
|
|
|
other.data = 0;
|
|
|
|
other.type = Null;
|
|
|
|
}
|
|
|
|
JValue &operator=(JValue &&other) noexcept {
|
|
|
|
std::swap(data, other.data);
|
|
|
|
std::swap(type, other.type);
|
|
|
|
return *this;
|
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
|
|
|
|
bool isNull() const {return type==Null;}
|
2023-09-19 11:49:20 +08:00
|
|
|
bool isStr() const {return type==Str;}
|
2023-07-21 17:40:49 +08:00
|
|
|
bool isObj() const {return type==Obj;}
|
|
|
|
bool isArray() const {return type==Array;}
|
|
|
|
|
|
|
|
bool toBool(bool def = false) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
return type==Null ? def : data;
|
2023-07-21 17:40:49 +08:00
|
|
|
}
|
|
|
|
int toInt(int def = 0) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
return toLong(def);
|
2023-07-21 17:40:49 +08:00
|
|
|
}
|
|
|
|
qint64 toLong(qint64 def = 0) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
if(type==Long || type==Bool) return data;
|
|
|
|
if(type==Double) return *(double*) &data;
|
|
|
|
if(type==Ulong) return *(quint64*) &data;
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
quint64 toULong(quint64 def = 0) const {
|
|
|
|
if(type==Ulong) return *(quint64*) &data;
|
|
|
|
if(type==Long || type==Bool) return data;
|
|
|
|
if(type==Double) return *(double*) &data;
|
2023-07-21 17:40:49 +08:00
|
|
|
return def;
|
|
|
|
}
|
|
|
|
double toDouble(double def = 0) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
if(type==Double) return *(double*) &data;
|
|
|
|
if(type==Long || type==Bool) return data;
|
|
|
|
if(type==Ulong) return *(quint64*) &data;
|
2023-07-21 17:40:49 +08:00
|
|
|
return def;
|
|
|
|
}
|
|
|
|
QString toStr(const QString &def = "") const {
|
2023-09-19 11:49:20 +08:00
|
|
|
if(type==Str) return (*(SharedData<QString>**) &data)->data;
|
|
|
|
if(type==Long) return QString::number(data);
|
|
|
|
if(type==Double) return QString::number(*(double*) &data);
|
|
|
|
if(type==Bool) return data ? "true" : "false";
|
|
|
|
if(type==Ulong) return QString::number(*(quint64*) &data);
|
2023-07-21 17:40:49 +08:00
|
|
|
return def;
|
|
|
|
}
|
2023-09-19 11:49:20 +08:00
|
|
|
QString toString(const QString &def = "") const {
|
|
|
|
return toStr(def);
|
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
JObj toObj() const {
|
2023-09-19 11:49:20 +08:00
|
|
|
if(type==Obj) return *(JObj*) &data;
|
2023-07-21 17:40:49 +08:00
|
|
|
return JObj();
|
|
|
|
}
|
|
|
|
JArray toArray() const {
|
2023-09-19 11:49:20 +08:00
|
|
|
if(type==Array) return *(JArray*) &data;
|
2023-07-21 17:40:49 +08:00
|
|
|
return JArray();
|
|
|
|
}
|
2023-09-19 11:49:20 +08:00
|
|
|
|
2023-08-07 09:04:53 +08:00
|
|
|
const JValue operator[](const QString &key) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
return type==Obj ? (*(const JObj*) &data)[key] : JValue();
|
2023-08-07 09:04:53 +08:00
|
|
|
}
|
|
|
|
const JValue operator[](int i) const {
|
2023-09-19 11:49:20 +08:00
|
|
|
return type==Array ? (*(const JArray*) &data)[i] : JValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
JArray::iterator begin() const noexcept {
|
|
|
|
return type==Array ? ((const JArray*) &data)->begin() : JArray::iterator();
|
|
|
|
}
|
|
|
|
JArray::iterator end() const noexcept {
|
|
|
|
return type==Array ? ((const JArray*) &data)->end() : JArray::iterator();
|
|
|
|
}
|
|
|
|
size_t size() const noexcept {
|
|
|
|
if(type==Array) return ((const JArray*) &data)->size();
|
|
|
|
else if(type==Obj) return ((const JObj*) &data)->size();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const JValue &other) const {
|
|
|
|
if(type==other.type) {
|
|
|
|
if(data==other.data) return true;
|
|
|
|
if(type==Null) return true;
|
|
|
|
if(type<=Double) return false;
|
|
|
|
if(type==Bool) return ((bool)data)==(bool)other.data;
|
|
|
|
if(type==Str) return (*(SharedData<QString>**) &data)->data==(*(SharedData<QString>**) &other.data)->data;
|
|
|
|
if(type==Obj) return *(JObj*) &data == *(JObj*) &other.data;
|
|
|
|
if(type==Array) return *(JArray*) &data == *(JArray*) &other.data;
|
|
|
|
} else {
|
|
|
|
if(type>Double || other.type>Double || type==Null || other.type==Null) return false;
|
|
|
|
if(type==Double || other.type==Double) return toDouble()==other.toDouble();
|
|
|
|
if(type==Long) return data==other.toLong();
|
|
|
|
else return toLong()==other.data;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool operator!=(const JValue &other) const {
|
|
|
|
return ! (*this==other);
|
2023-08-07 09:04:53 +08:00
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
private:
|
|
|
|
JValue(const void *) = delete; // avoid implicit conversions from char * to bool
|
|
|
|
};
|
|
|
|
|
2023-09-19 11:49:20 +08:00
|
|
|
QDebug operator<<(QDebug debug, const JValue &val);
|
|
|
|
|
2023-07-21 17:40:49 +08:00
|
|
|
class JParser {
|
|
|
|
public:
|
2023-08-07 09:04:53 +08:00
|
|
|
JParser(QTextStream &in) : in(in) {
|
|
|
|
#if(QT_VERSION_MAJOR < 6)
|
|
|
|
in.setCodec("UTF-8");
|
|
|
|
#endif
|
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
|
|
|
|
inline JValue read() {
|
|
|
|
skipSpace();
|
|
|
|
return readValue();
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
JValue readValue();
|
|
|
|
QString readStr();
|
|
|
|
void skipSpace();
|
|
|
|
|
|
|
|
QTextStream ∈
|
|
|
|
QChar ch{0}, bk{0};
|
|
|
|
};
|
|
|
|
|
|
|
|
inline JValue JFrom(const QByteArray &json, QString *err = 0) {
|
|
|
|
QTextStream in(json);
|
|
|
|
try {
|
|
|
|
return JParser(in).read();
|
|
|
|
} catch (QString anerr) {
|
|
|
|
if(err) *err = anerr;
|
|
|
|
} catch (const char *anerr) {
|
|
|
|
if(err) *err = anerr;
|
|
|
|
} catch (...) {
|
|
|
|
if(err) *err = "unknow error";
|
|
|
|
}
|
|
|
|
return JValue();
|
|
|
|
}
|
|
|
|
inline JValue JFrom(QIODevice *device, QString *err = 0) {
|
|
|
|
QTextStream in(device);
|
|
|
|
try {
|
|
|
|
return JParser(in).read();
|
|
|
|
} catch (QString anerr) {
|
|
|
|
if(err) *err = anerr;
|
|
|
|
} catch (const char *anerr) {
|
|
|
|
if(err) *err = anerr;
|
|
|
|
} catch (...) {
|
|
|
|
if(err) *err = "unknow error";
|
|
|
|
}
|
|
|
|
return JValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
class JOut {
|
|
|
|
public:
|
2023-08-07 09:04:53 +08:00
|
|
|
JOut(QTextStream &out, QString indent = "") : out(out), indent(indent) {
|
|
|
|
#if(QT_VERSION_MAJOR < 6)
|
|
|
|
out.setCodec("UTF-8");
|
|
|
|
#endif
|
|
|
|
}
|
2023-07-21 17:40:49 +08:00
|
|
|
|
2023-09-19 11:49:20 +08:00
|
|
|
void write(const JValue &value);
|
2023-07-21 17:40:49 +08:00
|
|
|
void writeStr(const QString &str);
|
|
|
|
void writeMap(const JObj &map);
|
|
|
|
void writeList(const JArray &objs);
|
|
|
|
protected:
|
|
|
|
QTextStream &out;
|
|
|
|
QString indent;
|
|
|
|
int cnt{0};
|
|
|
|
};
|
|
|
|
|
2023-09-19 11:49:20 +08:00
|
|
|
inline QString JToStr(const JValue &value, QString indent = "") {
|
2023-07-21 17:40:49 +08:00
|
|
|
QString json;
|
|
|
|
QTextStream out(&json);
|
2023-09-19 11:49:20 +08:00
|
|
|
JOut(out, indent).write(value);
|
2023-07-21 17:40:49 +08:00
|
|
|
return json;
|
|
|
|
}
|
2023-09-19 11:49:20 +08:00
|
|
|
inline QByteArray JToBytes(const JValue &value, QString indent = "") {
|
2023-07-21 17:40:49 +08:00
|
|
|
QByteArray json;
|
|
|
|
QTextStream out(&json);
|
2023-09-19 11:49:20 +08:00
|
|
|
JOut(out, indent).write(value);
|
2023-07-21 17:40:49 +08:00
|
|
|
return json;
|
|
|
|
}
|
2023-09-19 11:49:20 +08:00
|
|
|
inline QTextStream::Status JWrite(const JValue &value, QIODevice *device, QString indent = "") {
|
2023-07-21 17:40:49 +08:00
|
|
|
QTextStream out(device);
|
2023-09-19 11:49:20 +08:00
|
|
|
JOut(out, indent).write(value);
|
2023-07-21 17:40:49 +08:00
|
|
|
return out.status();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // QJSON_H
|