Json的易用性使得它成为最常用的通信格式之一,本文记录了Qt中的Json封装和解析,包括QJsonDocumentQJsonObjectQJsonValueQJsonArray类型;

QJson

直接看示例就会用了,一个示例json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"Company": "Digia",
"From": "1991",
"Name": "Qt",
"Page": {
"Developers": "https://www.qt.io/developers/",
"Download": "https://www.qt.io/download/",
"Home": "https://www.qt.io/"
},
"Version": [
4.8,
5.2,
5.7
]
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//封装出Json:
QJsonObject allJson; //一级键值
allJson.insert("Company","Digia");
allJson.insert("From","1991");
allJson.insert("Name","Qt");

QJsonObject pageJson; //插入二级键值
pageJson.insert("Developers","https://www.qt.io/developers/");
pageJson.insert("Download","https://www.qt.io/download/");
pageJson.insert("Home","https://www.qt.io/");

QJsonArray versionJson; //插值
versionJson.append(4.8);
versionJson.append(5.2);
versionJson.append(5.7);

allJson.insert("Page",pageJson); //二级键值
allJson.insert("Version",versionJson);

qDebug()<<allJson;

QJsonDocument doc(allJson);

QByteArray byteArray = doc.toJson(QJsonDocument::Indented); //Indented保留缩进、空格,易读;Compact则去除,高效;
//QString str(byteArray);

QDir dir("D:/Documents/Desktop/note");
QFile jsonFile(dir.filePath("test.json"));

if(jsonFile.open(QIODevice::WriteOnly|QIODevice::Text)){ //写入本地
jsonFile.write(byteArray);
jsonFile.close();
}

//解析示例Json:
QFile rJsonFile(dir.filePath("test.json"));
QByteArray rbyteArray;
if(rJsonFile.open(QIODevice::ReadOnly|QIODevice::Text)){
rbyteArray = rJsonFile.readAll();
rJsonFile.close();
}
if(rbyteArray.isEmpty())
qFatal("read error!!");

QJsonParseError jsonError;
QJsonDocument rdoc = QJsonDocument::fromJson(rbyteArray,&jsonError);
if(!rdoc.isNull()&&jsonError.error==QJsonParseError::NoError){
QJsonObject rallJson =rdoc.object();
QJsonValue cpnyValue = rallJson.value("Company");
qDebug()<<"Company:"<<cpnyValue.toString();

QJsonValue fromValue = rallJson.value("From");
qDebug()<<"From:"<<fromValue.toString();

QJsonObject PaegValue = rallJson.value("Page").toObject();
QJsonValue deveValye = PaegValue.value("Developers");
qDebug()<<"Developers:"<<deveValye.toString();
//...

QJsonValue versionValue = rallJson.value("Version");
QJsonArray versionArray = versionValue.toArray();
qDebug()<<"Version:";
for(int i=0; i<versionArray.size(); i++){
qDebug()<<versionArray[i].toDouble();
}
}
qDebug()<<"Done";

QJsonObject自定义类序列化与反序列化

对于自定义结构体/类使用QVariant再转换成Json编译上是没问题,实际上是无效的,例如:

1
2
3
4
5
6
7
///发送
Person person;
//.....赋值操作
json["person"] = QVariant::fromValue(person).toJsonValue();

///接收
Person person = wmsg.body["person"].toVariant().value<Person>();
此处toJsonValue()实际上没有起作用,其会失败但不会返回错误,导致发送时发送的是空类/空结构体,因此接收的也是错误的。

因此只能自定义序列化反序列化的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person
{
public:
int num = 0;

QJsonObject person2Json() const{
QJsonObject json;
json["num"] = num;
return json;
}

void json2Person(const QJsonObject& json){
num = json["num"].toInt();
}
};

发送时:把QJsonObject作为一种QJsonValue发送(兼容的)

1
2
///.....写入person类
wmsg.body["person"] = person.person2Json();

接收时:将Value转换成原格式QJsonObject:

1
2
person.json2Person(wmsg.body["person1"].toObject());
///.....读取person类

STL的序列化

更复杂的情况是使用了QMap/QVector等STL去装载类,分别使用QJsonObject/QJsonArray处理即可:

QMap uses QJsonObject:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
QMap<QString,Person> personMap;
QJsonObject personMap2json()const{
QJsonObject json;
QJsonObject personMapkv;
for(auto i = personMap.begin(); i!=personMap.end(); i++){
personMapkv.insert(i.key(),i.value().person2Json());
}
json.insert("personMap",personMapkv);
//......other variable
}

void json2personMap(const QJsonObject& json){
personMap.clear();
QJsonObject personMapkv = json["personMap"].toObject();
for(auto i=personMapkv.begin();i!=personMapkv.end();i++){
Person p;
p.json2Person(i.value().toObject());
personMap[i.key()] = p;
}
//......other variable
}

QVector uses QJsonArray:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
QVector<Person> personVec;

QJsonObject personVec2json()const{
QJsonObject json;
QJsonArray personVecArray;
for(const auto& i : personVec){
personVecArray.append(i.person2Json());
}
json.insert("personVec",personVecArray);
}

void json2personVec(const QJsonObject& json){
personVec.clear();
QJsonArray personVecArray = json["personMap"].toArray();
for(const auto& i:personVecArray){
Person p;
p.json2Person(i.toObject());
personVec.push_back(p);
}
}

QJsonObject同键插入

假如插入的相同的,不要有这种写法:

1
2
3
4
5
QJsonObject json;
QJsonArray array1{"hello"};
QJsonArray array2{"hello111111"};
json.insert("test", array1);
json.insert("test", array2);
这种写法的效果是test仅保留了hello111111,意味着json不仅能排除键的重复插入,而且仅保留最后一次插入的值

正确的写法是:

1
2
3
4
QJsonObject json;
QJsonArray array1{"hello"};
QJsonArray array2{"hello111111"};
json.insert("test",QJsonArray{array1,array2});
此时解析时:
1
2
QJsonArray tt1 = json["test"].toArray();
qDebug()<<tt1.size(); //输出的是2,不是1