Qt中的Json数据格式

JSON简介    

        JSON(JavaScrip Object Notation)是一种轻量级的数据交换格式。采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。总之,Json是一种数据格式,和语言无关,在什么语言中都可以使用Json。

基于这种通用的数据格式,一般处理两方面的任务:

  1. 组织数据(数据序列化),用于数据的网络传输
  2. 组织数据(数据序列化),写磁盘文件实现数据的持久化存储(一般以.json作为文件后缀);

Json中主要有两种数据格式Json数组Json对象,并且这两种格式可以交叉嵌套使用。数据简单用数组,复杂用对象。

 Json数组

        Json数组使用 [ ] 表示,[ ]里边是元素,元素和元素之间使用逗号间隔,最后一个元素后边没有逗号,一个Json数组中支持同时存在多种不同类型的成员,包括:整型、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null。由此可见Json数组比起C/C++数组要灵活很多。

eg:Json数组中的元素数据类型一致

// 整形
[1,2,3,4,5]
// 字符串
["luffy", "sanji", "zoro", "nami", "robin"]

eg:Json数组中的元素数据类型不一致

[12, 13.34, true, false, "hello,world", null]

eg:Json数组中的数组嵌套使用

[
    ["cat", "dog", "panda", "beer", "rabbit"],
    ["北京", "上海", "天津", "重庆"],
    ["luffy", "boy", 19]
]

eg:Json数组和对象嵌套使用

[
    {
        "luffy":{
            "age":19,
            "father":"Monkey·D·Dragon",
            "grandpa":"Monkey D Garp",
            "brother1":"Portgas D Ace",
            "brother2":"Sabo"
        }
    }
]

Json对象

        Json对象使用 {} 来描述,每个Json对象中可以存储若干个元素,每一个元素对应一个键值对(key:value 结构),元素和元素之间使用逗号间隔,最后一个元素后边没有逗号。对于每个元素中的键值对有以下细节需要注意:

        键值(key)必须是字符串,位于同一层级的键值不要重复(因为是通过键值取出对应的value值)value值的类型是可选的,可根据实际需求指定,可用类型包括:整型、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null。

eg:使用Json对象描述一个人的信息:

{
    "Name":"Ace",
    "Sex":"man",
    "Age":20,
    "Family":{
        "Father":"Gol·D·Roger",
        "Mother":"Portgas·D·Rouge",
        "Brother":["Sabo", "Monkey D. Luffy"]
    },
    "IsAlive":false,
    "Comment":"yyds"
}

 注意事项:

        通过上面的介绍可用看到,Json的结构虽然简单,但是进行嵌套之后就可以描述很复杂的事情,在项目开发过程中往往需要我们根据实际需求自己定义Json格式用来存储项目数据。另外,如果需要将Json数据持久化到磁盘文件中,需要注意一个问题:在一个Json文件中只能有一个Json数组或者Json对象的根节点,不允许同时存储多个并列的根节点注意最后一行结尾没有逗号,否则解析时会发生错误。下面举例说明:

错误的写法

// test.json
{
    "name":"luffy",
    "age":19
}
{
    "user":"ace",
    "passwd":"123456"
}

错误原因:在一个Json文件中有两个并列的Json根节点(并列包含Json对象和Json对象、Json对象和Json数组、Json数组和Json数组),根节点只能有一个

正确的写法

// test.json
{
    "Name":"Ace",
    "Sex":"man",
    "Age":20,
    "Family":{
        "Father":"Gol·D·Roger",
        "Mother":"Portgas·D·Rouge",
        "Brother":["Sabo", "Monkey D. Luffy"]
    },
    "IsAlive":false,
    "Comment":"yyds"
}

在上面的例子中通过Json对象以及Json数组的嵌套描述了一个人的身份信息,并且根节点只有一个就是Json对象,如果还需要使用Json数组或者Json对象描述其他信息,需要将这些信息写入到其他文件中,不要和这个Json对象并列写入到同一个文件里边,切记!

Qt中Json的操作

可以直接使用Qt提供的Json类进行数据的组织和解析

使用jsoncpp库解析json格式的数据,我们只需要掌握三个类:

  • Value 类:将json支持的数据类型进行了包装,最终得到一个Value类型
  • FastWriter类:将Value对象中的数据序列化为字符串
  • Reader类:反序列化, 将json字符串 解析成 Value 类型

QJsonValue

在Qt中QJsonValue可以封装的基础数据类型有六种(和Json支持的类型一致),分别为:

布尔类型:QJsonValue::Bool
浮点类型(包括整形): QJsonValue::Double
字符串类型: QJsonValue::String
Json数组类型: QJsonValue::Array
Json对象类型:QJsonValue::Object
空值类型: QJsonValue::Null
这个类型可以通过QJsonValue的构造函数被封装为一个类对象:

// Json对象
QJsonValue(const QJsonObject &o);
// Json数组
QJsonValue(const QJsonArray &a);
// 字符串
QJsonValue(const char *s);
QJsonValue(QLatin1String s);
QJsonValue(const QString &s);
// 整形 and 浮点型
QJsonValue(qint64 v);
QJsonValue(int v);
QJsonValue(double v);
// 布尔类型
QJsonValue(bool b);
// 空值类型
QJsonValue(QJsonValue::Type type = Null);

如果得到一个QJsonValue对象,如何判断内部封装的到底是什么类型的数据呢?这时候就需要调用相关的判断函数了,具体如下:

// 是否是Json数组
bool isArray() const;
// 是否是Json对象
bool isObject() const;
// 是否是布尔类型
bool isBool() const;
// 是否是浮点类型(整形也是通过该函数判断)
bool isDouble() const;
// 是否是空值类型
bool isNull() const;
// 是否是字符串类型
bool isString() const;
// 是否是未定义类型(无法识别的类型)
bool isUndefined() const;

通过判断函数得到对象内部数据的实际类型之后,如果有需求就可以再次将其转换为对应的基础数据类型,对应的API函数如下:

// 转换为Json数组
QJsonArray toArray(const QJsonArray &defaultValue) const;
QJsonArray toArray() const;
// 转换为布尔类型
bool toBool(bool defaultValue = false) const;
// 转换为浮点类型
double toDouble(double defaultValue = 0) const;
// 转换为整形
int toInt(int defaultValue = 0) const;
// 转换为Json对象
QJsonObject toObject(const QJsonObject &defaultValue) const;
QJsonObject toObject() const;
// 转换为字符串类型
QString toString() const;
QString toString(const QString &defaultValue) const;

 FastWriter 类

// 将数据序列化 -> 单行
// 进行数据的网络传输
std::string Json::FastWriter::write(const Value& root);

Reader 类

bool Json::Reader::parse(const std::string& document,
    Value& root, bool collectComments = true);
    参数:
        - document: json格式字符串
        - root: 传出参数, 存储了json字符串中解析出的数据
        - collectComments: 是否保存json字符串中的注释信息

// 通过begindoc和enddoc指针定位一个json字符串
// 这个字符串可以是完成的json字符串, 也可以是部分json字符串
bool Json::Reader::parse(const char* beginDoc, const char* endDoc,
    Value& root, bool collectComments = true);
	
// write的文件流  -> ofstream
// read的文件流   -> ifstream
// 假设要解析的json数据在磁盘文件中
// is流对象指向一个磁盘文件, 读操作
bool Json::Reader::parse(std::istream& is, Value& root, bool collectComments = true);

QJsonObject

        QJsonObject封装了Json中的对象,在里边可以存储多个键值对,为了方便操作,键值为字符串类型,值为QJsonValue类型。关于这个类的使用类似于C++中的STL类,仔细阅读API文档即可熟练上手使用,下面介绍一些常用API函数:

如何创建空的Json对象
QJsonObject::QJsonObject();	// 构造空对象
将键值对添加到空对象中
iterator QJsonObject::insert(const QString &key, const QJsonValue &value);
获取对象中键值对个数
int QJsonObject::count() const;
int QJsonObject::size() const;
int QJsonObject::length() const;
通过key得到value
QJsonValue QJsonObject::value(const QString &key) const;    // utf8
QJsonValue QJsonObject::value(QLatin1String key) const;	    // 字符串不支持中文
QJsonValue QJsonObject::operator[](const QString &key) const;
QJsonValue QJsonObject::operator[](QLatin1String key) const;
删除键值对
void QJsonObject::remove(const QString &key);
QJsonValue QJsonObject::take(const QString &key);	// 返回key对应的value值
通过key进行查找
iterator QJsonObject::find(const QString &key);
bool QJsonObject::contains(const QString &key) const;
遍历,方式有三种:
  • 使用相关的迭代器函数
  • 使用 [] 的方式遍历, 类似于遍历数组, []中是键值
  • 先得到对象中所有的键值, 在遍历键值列表, 通过key得到value值
QStringList QJsonObject::keys() const;

QJsonArray

        QJsonArray封装了Json中的数组,在里边可以存储多个元素,为了方便操作,所有的元素类统一为QJsonValue类型。关于这个类的使用类似于C++中的STL类,仔细阅读API文档即可熟练上手使用,下面介绍一些常用API函数:

创建空的Json数组
QJsonArray::QJsonArray();
QJsonArray::QJsonArray();
void QJsonArray::append(const QJsonValue &value);	// 在尾部追加
void QJsonArray::insert(int i, const QJsonValue &value); // 插入到 i 的位置之前
iterator QJsonArray::insert(iterator before, const QJsonValue &value);
void QJsonArray::prepend(const QJsonValue &value); // 添加到数组头部
void QJsonArray::push_back(const QJsonValue &value); // 添加到尾部
void QJsonArray::push_front(const QJsonValue &value); // 添加到头部
计算数组元素的个数
int QJsonArray::count() const;
int QJsonArray::size() const;
从数组中取出某一个元素的值
QJsonValue QJsonArray::at(int i) const;
QJsonValue QJsonArray::first() const; // 头部元素
QJsonValue QJsonArray::last() const; // 尾部元素
QJsonValueRef QJsonArray::operator[](int i);
删除数组中的某一个元素
iterator QJsonArray::erase(iterator it);    // 基于迭代器删除
void QJsonArray::pop_back();           // 删除尾部
void QJsonArray::pop_front();          // 删除头部
void QJsonArray::removeAt(int i);      // 删除i位置的元素
void QJsonArray::removeFirst();        // 删除头部
void QJsonArray::removeLast();         // 删除尾部
QJsonValue QJsonArray::takeAt(int i);  // 删除i位置的原始, 并返回删除的元素的值
Josn数组的遍历,常用的方式有两种:
  • 可以使用迭代器进行遍历(和使用迭代器遍历STL容器一样)
  • 可以使用数组的方式遍历

QJsonDocument

        它封装了一个完整的JSON文档,并且可以从UTF-8编码的基于文本的表示以及Qt自己的二进制格式读取和写入该文档。QJsonObject 和 QJsonArray这两个对象中的数据不能直接转换为字符串类型的,如果要进行数据传输或者数据的持久化,操作的都是字符串类型而不是QJsonObject 或者 QJsonArray类型,我们需要通过一个Json文档类进行二者之间的转换

序列化:把一个json格式的数据块转化成我们能阅读的字符串。

反序列化:把一个字符串转化成json格式。

下面依次介绍一下这两个转换流程应该如何操作:

QJsonObject 或者 QJsonArray ===> 字符串
1.创建QJsonDocument对象
QJsonDocument::QJsonDocument(const QJsonObject &object);
QJsonDocument::QJsonDocument(const QJsonArray &array);

可以看出,通过构造函数就可以将实例化之后的**QJsonObject 或者 QJsonArray **转换为QJsonDocument对象了。

2.将文件对象中的数据进行序列化
// 二进制格式的json字符串
QByteArray QJsonDocument::toBinaryData() const;	 
// 文本格式
QByteArray QJsonDocument::toJson(JsonFormat format = Indented) const;	

通过调用toxxx()方法就可以得到文本格式或者二进制格式的Json字符串了。

3.使用得到的字符串进行数据传输, 或者磁盘文件持久化
字符串 ===> QJsonObject 或者 QJsonArray

一般情况下,通过网络通信或者读磁盘文件就会得到一个Json格式的字符串,如果想要得到相关的原始数据就需要对字符串中的数据进行解析,具体解析流程如下:

1.将得到的Json格式字符串通过 QJsonDocument 类的静态函数转换为QJsonDocument类对象
[static] QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation = Validate);
// 参数文件格式的json字符串
[static] QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error = Q_NULLPTR);
2.将文档对象转换为json数组/对象
// 判断文档对象中存储的数据是不是数组
bool QJsonDocument::isArray() const;
// 判断文档对象中存储的数据是不是json对象
bool QJsonDocument::isObject() const
    
// 文档对象中的数据转换为json对象
QJsonObject QJsonDocument::object() const;
// 文档对象中的数据转换为json数组
QJsonArray QJsonDocument::array() const;
3.通过调用QJsonArray , QJsonObject 类提供的 API 读出存储在对象中的数据。

关于Qt中Json数据对象以及字符串之间的转换的操作流程是固定的,我们在编码过程中只需要按照上述模板处理即可,相关的操作是没有太多的技术含量可言的。

Json读取解析数据示例代码

比如:我们要将下面这个Json数组写入的一个文件中

[
    12, 
    12.34, 
    true, 
    "tom", 
    ["jack", "ace", "robin"], 
    {"sex":"man", "girlfriend":"lucy"}
]
#include <json/json.h>
#include <fstream>
using namespace Json;

int main()
{
    writeJson();
    readJson();
}
 写json文件
void writeJson()
{
    // 将最外层的数组看做一个Value
    // 最外层的Value对象创建
    Value root;
    // Value有一个参数为int 行的构造函数
    root.append(12);	// 参数进行隐式类型转换
    root.append(12.34);
    root.append(true);
    root.append("tom");
    
    // 创建并初始化一个子数组
    Value subArray;
    subArray.append("jack");
    subArray.append("ace");
    subArray.append("robin");
    root.append(subArray);
    
    // 创建并初始化子对象
    Value subObj;
    subObj["sex"] = "woman";  // 添加键值对
    subObj["girlfriend"] = "lucy";
    root.append(subObj);
    
    // 序列化
#if 1
    // 有格式的字符串
    string str = root.toStyledString();
#else
    //使用类,转换成不带格式的字符串
    FastWriter f;
    string str = f.write(root);
#endif
    // 将序列化的字符串写磁盘文件
    ofstream ofs("test.json");
    ofs << str;
    ofs.close();
}
 读json文件 
void readJson()
{
    // 1. 将磁盘文件中的json字符串读到磁盘文件
    ifstream ifs("test.json");
    // 2. 反序列化 -> value对象
    Value root;
    Reader r;
    r.parse(ifs, root);
    // 3. 从value对象中将数据依次读出
    if (root.isArray())
    {
        // 数组, 遍历数组
        for (int i = 0; i < root.size(); ++i)
        {
            // 依次取出各个元素, 类型是value类型
            Value item = root[i];
            // 判断item中存储的数据的类型
            if (item.isString())
            {
                cout << item.asString() << ", ";
            }
            else if (item.isInt())
            {
                cout << item.asInt() << ", ";
            }
            else if (item.isBool())
            {
                cout << item.asBool() << ", ";
            }
            else if (item.isDouble())
            {
                cout << item.asFloat() << ", ";
            }
            else if (item.isArray())
            {
                for (int j = 0; j < item.size(); ++j)
                {
                    cout << item[j].asString() << ", ";
                }
            }
            else if (item.isObject())
            {
                // 对象
                // 得到所有的key
                Value::Members keys = item.getMemberNames();
                for (int k = 0; k < keys.size(); ++k)
                {
                    cout << keys.at(k) << ":" << item[keys[k]] << ", ";
                }
            }
            
    	}
        cout << endl;
    }
}
 注意:
  • 一般情况下,对于Json字符串的解析函数都是有针对性的,因为需求不同设计的Json格式就会有所不同,所以不要试图写出一个通用的Json解析函数,这样只会使函数变得臃肿而且不易于维护,每个Json格式对应一个相应的解析函数即可。
  • 上面的例子中为了给大家演示Qt中Json类相关API函数的使用将解析步骤写的复杂了,因为在解析的时候我们是知道Json对象中的所有key值的,可以直接通过key值将对应的value值取出来,因此上面程序中的一些判断和循环其实是可以省去的。
  • 虽然Json这种格式无外乎数组和对象两种,但是需求不同我们设计的Json文件的组织方式也不同,一般都是特定的文件对应特定的解析函数,一个解析函数可以解析任何的Json文件这种设计思路是坚决不推荐的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值