Qt中使用JSON

722 阅读2分钟

前言

json是一种对源自Javascript的对象数据进行编码的格式,但现在广泛用于互联网上的数据交换格式。Qt5中提供了易于使用的C++ API来解析、修改和保存json数据。

Qt中提供了5个类来操作json,下面是类信息:

类名类描述
QJsonObject封装了json对象
QJsonArray封装了json数组
QJsonValue封装了json的值
QJsonDocument提供了json文件读写的方式
QJsonParseError这个类被用于在解析json报告错误

QJsonValue为json提供了6种基础数据类型,分别是

  • bool
  • double
  • string
  • array
  • object
  • null

Qt读取json文件

{
  "teacher_name": "wangwu",
  "teacher_age": 30,
  "student": [
    {
      "name": "zhangsan",
      "age": 12
    },
    {
      "name": "lisi",
      "age": 14
    },
    {
      "name": "zhaoliu",
      "age": 13
    }
  ]
}
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

struct Student {
	QString strName;
	int dwAge = 0;
};

struct Course
{
	QString strTeacherName;
	int dwTeacherAge = 0;
	QVector<Student> students;

	void debug() const;
};

void Course::debug() const
{
	qDebug() << "teacher name is " << strTeacherName << ", age is " << dwTeacherAge;
	for (const Student & curStudent : students)
	{
		qDebug() << "\t student name is " << curStudent.strName << ", age is " << curStudent.dwAge;
	}
}

void loadJsonFile()
{
	QFile loadFile("D://config.json");
	if (!loadFile.open(QIODevice::ReadOnly))
	{
		qDebug() << "open file " << loadFile.fileName() << " failed";
		return;
	}

	QByteArray allData = loadFile.readAll();
	loadFile.close();

	QJsonParseError jsonError;
	QJsonDocument jsonDoc = QJsonDocument::fromJson(allData, &jsonError);

	if (jsonError.error != QJsonParseError::NoError)
	{
		qDebug() << "parse json object failed, " << jsonError.errorString();
		return;
	}

	QJsonObject jsonObj = jsonDoc.object();

	Course curCourse;
	curCourse.strTeacherName = jsonObj.value("teacher_name").toString();
	curCourse.dwTeacherAge = jsonObj.value("teacher_age").toInt();

	if (jsonObj.value("student").isArray())
	{
		QJsonArray jsonArray = jsonObj.value("student").toArray();
        
        //对QJsonArray进行排序
        std::sort(jsonArray.begin(), jsonArray.end(), [](const QJsonValue & lhs, const QJsonValue & rhs) -> bool {
			return lhs.toObject().value("age").toInt() < rhs.toObject().value("age").toInt();
		});
        
		for (const QJsonValue jsonStu : jsonArray)
		{
			Student curStudent;
			curStudent.strName = jsonStu.toObject().value("name").toString();
			curStudent.dwAge = jsonStu.toObject().value("age").toInt();
			
			curCourse.students.push_back(curStudent);
		}
	}

	curCourse.debug();
}
  1. vs2017编译运行

    如果服务器传过来一个乱序的QJsonArray,我们需要自己对QJsonArray排序,可以使用上面的std::sort进行排序,程序输出结果如下:

    20210413163206.png

    如果把std::sort换成qSort,程序输出结果就变成了这样:

    20210413162506.png

    一脸懵逼,难道是Qt5中的qSort有BUG?

  2. qtcreator编译运行

    qtcreator直接编译报错,找不到swap(QJsonValueRef, QJsonValueRef)对应的函数,下面是报错信息:

    20210415154927.png

    通过这些信息发现,上面的7个重载版本都是用Q_DECLARE_SHARED包裹起来的,这个宏展开后会得到这样的代码

    inline void swap(TYPE &value1, TYPE &value2) noexcept
    { 
    	value1.swap(value2); 
    }
    

    我试图Q_DECLARE_SHARED(QJsonValueRef),可惜QJsonValueRef对象并没有swap成员函数。然后尝试重载一个swap函数。

    void swap(QJsonValue & lhs, QJsonValue & rhs) noexcept
    {
        qSwap(lhs, rhs);
    }
    

    这样编译不会报错,可以输出正确的结果,但是断点调试发现,程序并没有调用到我重载的函数,也是很神奇。

Qt写入json文件

#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

struct Student {
	QString strName;
	int dwAge = 0;
};

struct Course
{
	QString strTeacherName;
	int dwTeacherAge = 0;
	QVector<Student> students;
};

Course curCourse = { "zhangsan", 40, { {"lisi", 20}, {"wangwu", 21}, {"zhaoliu", 22}, } };

void saveJsonFile()
{
	QFile writeFile("D://config.json");
	if (!writeFile.open(QIODevice::WriteOnly))
	{
		qDebug() << "open file " << writeFile.fileName() << " failed";
		return;
	}

	QJsonObject jsonObj;
	jsonObj.insert("teacher_name", curCourse.strTeacherName);
	jsonObj.insert("teacher_age", curCourse.dwTeacherAge);

	QJsonArray jsonArray;
	for (const Student curStudent : curCourse.students)
	{
		QJsonObject jsonStu;
		jsonStu.insert("name", curStudent.strName);
		jsonStu.insert("age", curStudent.dwAge);

		jsonArray.push_back(jsonStu);
	}
	jsonObj.insert("student", jsonArray);

	QJsonDocument jsonDoc;
	jsonDoc.setObject(jsonObj);

	writeFile.write(jsonDoc.toJson(QJsonDocument::Compact));
	writeFile.close();
}

{"student":[{"age":20,"name":"lisi"},{"age":21,"name":"wangwu"},{"age":22,"name":"zhaoliu"}],"teacher_age":40,"teacher_name":"zhangsan"}
枚举类型字段详细描述
QJsonDocument::Indented(默认值)以人类可读的方式序列化数据
QJsonDocument::Compact以紧凑的方式序列化数据,会去除空格和换行符

Qt读写json二进制文件

QJsonDocument支持将json对象序列化成二进制的形式进行读写,这种方式的优点就是可以直接"mmap"更快的访问,缺点就是不易阅读。

  1. 写入二进制文件

    //上例中的writeFile.write(jsonDoc.toJson(QJsonDocument::Compact));替换成下面这句
    writeFile.write(jsonDoc.toBinaryData());
    
  2. 读取二进制文件

    //QJsonParseError jsonError;
    //QJsonDocument jsonDoc = QJsonDocument::fromJson(allData, &jsonError);
    
    //if (jsonError.error != QJsonParseError::NoError)
    //{
    //	qDebug() << "parse json object failed, " << jsonError.errorString();
    //	return;
    //}
    
    //把上例中的代码注释掉,换成下面的代码就可以了
    QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(allData);
    
    if (!jsonDoc.isObject())
    {
    	qDebug() << "parse json object failed, ";
    	return;
    }