JSON是一种来自Javascript的对象数据的编码格式,现在被广泛用作互联网上的数据交换格式。
json数据类型有bool、double、string、object、array、null
json对象以{key:value,…}格式表示
json数组以[]括起来表示
示例使用
Qt自然也提供了对json的读写支持
相关的类有
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
我们举个例子来看这几个类如何使用
假设我们需要保存一个4画面的ui布局,还有其他一些相关信息。保存json格式如下:
{
"bShowTitle": true,
"fps": 25,
"username": "admin",
"wnds": [
{
"bVisible": true,
"h": 300,
"w": 400,
"wndid": 1,
"x": 0,
"y": 0
},
{
"bVisible": true,
"h": 300,
"w": 400,
"wndid": 2,
"x": 400,
"y": 0
},
{
"bVisible": true,
"h": 300,
"w": 400,
"wndid": 3,
"x": 0,
"y": 300
},
{
"bVisible": true,
"h": 300,
"w": 400,
"wndid": 4,
"x": 400,
"y": 300
}
]
}
我们使用Qt提供的json类来解析和生成这个json文件
#ifndef HSAVEUI_H
#define HSAVEUI_H
#include <QString>
#include <QFile>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
struct WndInfo{
int wndid;
int x;
int y;
int w;
int h;
bool bVisible;
};
struct MonitorUI{
QString username;
int fps;
bool bShowTitle; // 是否显示标题
WndInfo wnds[4];
};
class HSaveUI
{
public:
HSaveUI(QString filepath){
m_filepath = filepath;
}
public:
bool read(MonitorUI& ui){
QFile file(m_filepath);
if (!file.open(QIODevice::ReadOnly)){
qWarning("could not open file:%s!", m_filepath.toLocal8Bit().data());
return false;
}
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err);
if (err.error != QJsonParseError::NoError){
qWarning("Parse err:%d %s!", err.error, err.errorString().toLocal8Bit().data());
return false;
}
QJsonObject obj = doc.object();
ui.username = obj["username"].toString();
ui.bShowTitle = obj["bShowTitle"].toBool();
ui.fps = obj["fps"].toInt();
QJsonArray arr = obj["wnds"].toArray();
for (int i = 0; i < arr.size(); ++i){
QJsonObject wnd = arr[i].toObject();
ui.wnds[i].wndid = wnd["wndid"].toInt();
ui.wnds[i].bVisible = wnd["bVisible"].toBool();
ui.wnds[i].x = wnd["x"].toInt();
ui.wnds[i].y = wnd["y"].toInt();
ui.wnds[i].w = wnd["w"].toInt();
ui.wnds[i].h = wnd["h"].toInt();
}
return true;
}
bool write(MonitorUI& ui){
QJsonArray wnds;
for (int i = 0; i < 4; ++i){
QJsonObject wnd;
wnd["wndid"] = ui.wnds[i].wndid;
wnd["bVisible"] = ui.wnds[i].bVisible;
wnd["x"] = ui.wnds[i].x;
wnd["y"] = ui.wnds[i].y;
wnd["w"] = ui.wnds[i].w;
wnd["h"] = ui.wnds[i].h;
wnds.append(wnd);
}
QJsonObject obj;
obj["username"] = ui.username;
obj["bShowTitle"] = ui.bShowTitle;
obj["fps"] = ui.fps;
obj["wnds"] = wnds;
QJsonDocument doc;
doc.setObject(obj);
QByteArray json = doc.toJson();
QFile file(m_filepath);
if (!file.open(QIODevice::WriteOnly)){
qWarning("could not save file!");
return false;
}
file.write(json);
return true;
}
private:
QString m_filepath;
};
#endif // HSAVEUI_H
QJsonDocment的fromJson方法可以解析json字符串,object方法返回json对象,toJson方法构造一个json格式字符串
QJsonObject表示json对象,通过key索引即可得到value,如obj[“username”]
QJsonArray表示json数组,通过size方法可以获取到数组大小,[]索引方法获取到每一个数组元素
QJsonValue用来封装json的数据类型(bool、double、string、object、array、null),并提供了对应的转换方法
bool toBool(bool defaultValue = false) const;
int toInt(int defaultValue = 0) const;
double toDouble(double defaultValue = 0) const;
QString toString() const;
QString toString(const QString &defaultValue) const;
QJsonArray toArray() const;
QJsonArray toArray(const QJsonArray &defaultValue) const;
QJsonObject toObject() const;
源码赏析
主要研究Qt中如何解析json的,QJsonDocument::fromJson会调用Parser::parse
QJsonDocument Parser::parse(QJsonParseError *error)
{
#ifdef PARSER_DEBUG
indent = 0;
qDebug(">>>>> parser begin");
#endif
// allocate some space
dataLength = qMax(end - json, (ptrdiff_t) 256);
data = (char *)malloc(dataLength);
// fill in Header data
QJsonPrivate::Header *h = (QJsonPrivate::Header *)data;
h->tag = QJsonDocument::BinaryFormatTag;
h->version = 1u;
current = sizeof(QJsonPrivate::Header);
eatBOM();
char token = nextToken();
DEBUG << hex << (uint)token;
if (token == BeginArray) {
if (!parseArray())
goto error;
} else if (token == BeginObject) {
if (!parseObject())
goto error;
} else {
lastError = QJsonParseError::IllegalValue;
goto error;
}
eatSpace();
if (json < end) {
lastError = QJsonParseError::GarbageAtEnd;
goto error;
}
END;
{
if (error) {
error->offset = 0;
error->error = QJsonParseError::NoError;
}
QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current);
return QJsonDocument(d);
}
error:
#ifdef PARSER_DEBUG
qDebug(">>>>> parser error");
#endif
if (error) {
error->offset = json - head;
error->error = lastError;
}
free(data);
return QJsonDocument();
}
首先eatBOM,去掉可能的BOM格式的三个字节
void Parser::eatBOM()
{
// eat UTF-8 byte order mark
uchar utf8bom[3] = { 0xef, 0xbb, 0xbf };
if (end - json > 3 &&
(uchar)json[0] == utf8bom[0] &&
(uchar)json[1] == utf8bom[1] &&
(uchar)json[2] == utf8bom[2])
json += 3;
}
然后nextToken,寻找到下一个记号,记号有{对象}[数组] key:value,“string”在枚举中可以看到定义
enum {
Space = 0x20,
Tab = 0x09,
LineFeed = 0x0a,
Return = 0x0d,
BeginArray = 0x5b,
BeginObject = 0x7b,
EndArray = 0x5d,
EndObject = 0x7d,
NameSeparator = 0x3a,
ValueSeparator = 0x2c,
Quote = 0x22
};
char Parser::nextToken()
{
if (!eatSpace())
return 0;
char token = *json++;
switch (token) {
case BeginArray:
case BeginObject:
case NameSeparator:
case ValueSeparator:
case EndArray:
case EndObject:
eatSpace();
case Quote:
break;
default:
token = 0;
break;
}
return token;
}
// 去掉空格
bool Parser::eatSpace()
{
while (json < end) {
if (*json > Space)
break;
if (*json != Space &&
*json != Tab &&
*json != LineFeed &&
*json != Return)
break;
++json;
}
return (json < end);
}
nextToken遇到对象开始符{
就开始解析对象parseObject,遇到数组开始符[
就解析数组parseArray.
parseObject和parseArray的过程类似,遇到标识符就解析对应的东西,主要有解析值parseValue,解析字符串parseArray,解析数字parseNumber。有兴趣的可以深挖下源码。