Qt读写JSON,以及使用QTreeView展示和编辑JSON数据

0.前言

JSON(JavaScript Object Notation)是一种轻量级的结构化数据格式,相对于XML语法更简洁。它具有6种基本数据类型:bool(true或false字符串表示)、double(对应JS中number)、string、array(值的列表)、object(键值对集合)、null 。

虽然cJSON和JsonCpp也是常用的C/C++ JSON解析器,但是在Qt框架中一般用Qt提供的解析类就行了。本文主要介绍QJson的使用,以及使用QTreeView展示和编辑JSON文档(代码链接在最后)。

目录

1.了解QJson

2.解析JSON

3.生成JSON

4.利用QTreeView展示和编辑JSON

5.参考


《偶然》 ----徐志摩

我是天空里的一片云,
偶尔投影在你的波心──
你不必讶异,
更无须欢喜──
在转瞬间消灭了踪影。

你我相逢在黑夜的海上,
你有你的,我有我的,方向;
你记得也好,
最好你忘掉
在这交会时互放的光亮!

1.了解QJson

Qt提供了多个类来分别表示JSON文档、节点、迭代器,这些类都是隐式共享的。

QJsonDocument:用于读写JSON文档。它包装完整的JSON文档,并且可以从基于UTF-8编码的文本表示形式以及Qt自己的二进制格式读取和写入此文档。可以使用QJsonDocument::fromJson() 将JSON文档从其基于文本的表示形式转换为QJsonDocument.toJson() 将其转换回文本。

QJsonParseError:用于表示JSON解析时发生的错误。

QJsonObject:表示键值对列表,其中键是唯一的字符串,而值由QJsonValue表示。QJson还提供了iterator和const_iterator来对QJsonObject进行迭代。要注意的是,一般的JSON结构,object键值对列表是无序的,array才有序,QJSON实现没有提供object有序的设置。

QJsonArray:表示值的列表,值由QJsonValue表示。

QJsonValue:存储数据的值,具有六种基本类型,可通过type() 方法获取其类型枚举:Bool、Double、String、Array、Object、Null,外加一个Undefined表示不确定的值。

在使用这些类时,先添加上对应的头文件。

2.解析JSON

一般常用的操作是读取文件用QByteArray初始化一个QJsonDocument,或者接收来自Http的object、array。下面的代码操作步骤为:读取文件,解析为QJsonDocument,遍历其中的QJsonObject和QJsonArray并获取值。操作起来相当的简单。

#include <QFile>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QDebug>

//读取并解析JSON文档
bool LoadJson(const QString &filepath)
{
    //判断路径以及是否正常打开
    if(filepath.isEmpty())
        return false;
    QFile file(filepath);
    if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
        return false;

    //读取数据后关闭文件
    const QByteArray raw_data=file.readAll();
    file.close();

    //解析为Json文档
    QJsonParseError json_error;
    QJsonDocument json_doc=QJsonDocument::fromJson(raw_data,&json_error);

    //是否正常解析Json数据
    if(json_doc.isNull()||json_doc.isEmpty()||json_error.error!=QJsonParseError::NoError)
        return false;

    //判断是object{}还是array[]的Json
    if(json_doc.isObject()){
        //解析文档中的Object
        const QJsonObject json_obj=json_doc.object();

        //[1]可以遍历object
        QJsonObject::const_iterator iter;
        for (iter = json_obj.constBegin(); iter != json_obj.constEnd(); ++iter){
            qDebug()<<iter.key();
            const QJsonValue iter_val=iter.value();
            switch (iter_val.type()) {
            case QJsonValue::Object:
                //如果是Object继续递归解析
                break;
            case QJsonValue::Array:
                //如果是Array继续递归解析
                break;
            case QJsonValue::Bool:
                qDebug()<<iter_val.toBool();
                break;
            case QJsonValue::Double:
                qDebug()<<iter_val.toDouble();
                break;
            case QJsonValue::String:
                qDebug()<<iter_val.toString();
                break;
            case QJsonValue::Null: break;
            case QJsonValue::Undefined: break;
            default: break;
            }
        }

        //[2]也可以根据key查询object中的值
        if(json_obj.contains("key_name")){
            const QJsonValue json_val=json_obj.value("key_name");
            if(json_val.isString()){
                const QString str_val=json_val.toString();
            }else if(json_val.isArray()){
                //遍历数组
            }
        }
    }else if(json_doc.isArray()){
        //解析文档中的Array
        const QJsonArray json_arr=json_doc.array();

        //遍历array
        QJsonArray::const_iterator iter;
        for (iter = json_arr.constBegin(); iter != json_arr.constEnd(); ++iter){
            qDebug()<<iter.i;
            const QJsonValue iter_val=*iter;
            if(iter_val.isString()){
                qDebug()<<iter_val.toString();
            }
        }

        for(int i=0;i<json_arr.count();i++){
            qDebug()<<i;
            const QJsonValue iter_val=json_arr.at(i);
            if(iter_val.isString()){
                qDebug()<<iter_val.toString();
            }
        }
    }

    return true;
}

3.生成JSON

生成和解析操作一样也很简单。

//生成JSON文档并导出
bool DumpJson(const QString &filepath)
{
    //判断路径
    if(filepath.isEmpty())
        return false;

    //这里构造一个Json,并添加模拟的数据
    QJsonDocument json_doc;

    QJsonArray json_arr;
    json_arr.insert(0,"Gong");
    json_arr.insert(1,"Jian");
    json_arr.insert(2,"Bo");
    json_arr.append("1992");

    QJsonObject json_obj;
    json_obj.insert("name","Qt");
    QJsonObject json_subobj{
        {"major",5},
        {"minor",12},
        {"patch",6}
    };
    //可以嵌套
    json_obj.insert("version",json_subobj);
    json_obj.insert("array",json_arr);
    
    //可以是object或者array
    json_doc.setObject(json_obj);

    //转换为bytearray,Compact没有换行,Indented有换行可读性更强
    QByteArray json_data=json_doc.toJson(QJsonDocument::Indented);

    //判断是否正常打开
    QFile file(filepath);
    if(!file.open(QIODevice::WriteOnly))
        return false;
    file.write(json_data);
    file.close();

    return true;
}

QJsonDocument的toJson()接口有两个格式化参数,QJsonDocument::Compact更紧凑没有换行缩进等:

QJsonDocument::Indented 是更容易查看的带换行的格式:

4.利用QTreeView展示和编辑JSON

用QTreeView来展示JSON数据还是比较常见的,我写了一个简单的展示和编辑JSON数据的Demo(增删部分因为涉及到delegate,我就懒得写了,但是大致的框架是有的)。

其实思路很简单,解析并生成Tree就像生成二叉树:

void JsonTreeModel::parseObject(const QString &key, const QJsonObject &obj, JsonTreeItem *&item)
{
    //构造Object节点
    JsonTreeItem *child=new JsonTreeItem({{0,key},{1,"[Object]"}},JsonTreeItem::Object,item);
    item->appendChild(child);

    //遍历Object的keys
    const QStringList keys=obj.keys();
    for(const QString &item_key:keys){
        //qDebug()<<"key:"<<item_key;
        parseValue(item_key,obj.value(item_key),child);
    }
}

void JsonTreeModel::parseArray(const QString &key, const QJsonArray &arr, JsonTreeItem *&item)
{
    //构造Array节点
    JsonTreeItem *child=new JsonTreeItem({{0,key},{1,"[Array]"}},JsonTreeItem::Array,item);
    item->appendChild(child);

    //遍历Array
    for(int i=0;i<arr.count();i++){
        parseValue("-",arr.at(i),child);
    }
}

void JsonTreeModel::parseValue(const QString &key, const QJsonValue &val, JsonTreeItem *&item)
{
    QVariant the_val;
    //根据Value的类型进行下一步处理
    //如果是Object或Array就继续递归
    //如果是值就添加一个节点
    switch (val.type()) {
    case QJsonValue::Object:
        parseObject(key,val.toObject(),item);
        return;
        break;
    case QJsonValue::Array:
        parseArray(key,val.toArray(),item);
        return;
        break;
    case QJsonValue::Bool:
        the_val=val.toBool();
        break;
    case QJsonValue::Double:
        the_val=val.toDouble();
        break;
    case QJsonValue::String:
        the_val=val.toString();
        break;
    case QJsonValue::Null: break;
    case QJsonValue::Undefined: break;
    default: break;
    }

    //构造子节点
    JsonTreeItem *child=new JsonTreeItem({{0,key},{1,the_val}},JsonTreeItem::Value,item);
    item->appendChild(child);
}

导出编辑完后的Tree就像遍历二叉树:

QVariantMap JsonTreeModel::dumpObject(JsonTreeItem *&item) const
{
    QVariantMap json_obj; //QVariantMap对应QJsonObject
    const int child_count=item->childCount();
    for(int i=0;i<child_count;i++){
        JsonTreeItem *child=item->childItem(i);
        if(!child) continue;
        //为什么不用一个返回QVariant的函数之类的封装下?
        switch (child->type()) {
        case JsonTreeItem::Object:
            json_obj.insert(child->key(),dumpObject(child));
            break;
        case JsonTreeItem::Array:
            json_obj.insert(child->key(),dumpArray(child));
            break;
        case JsonTreeItem::Value:
            json_obj.insert(child->key(),dumpValue(child));
            break;
        default:
            break;
        }
    }
    return json_obj;
}

QVariantList JsonTreeModel::dumpArray(JsonTreeItem *&item) const
{
    QVariantList json_arr; //QVariantList对应QJsonArray
    const int child_count=item->childCount();
    for(int i=0;i<child_count;i++){
        JsonTreeItem *child=item->childItem(i);
        if(!child) continue;
        switch (child->type()) {
        case JsonTreeItem::Object:
            json_arr.append(dumpObject(child));
            break;
        case JsonTreeItem::Array:
            json_arr.append(dumpArray(child));
            break;
        case JsonTreeItem::Value:
            json_arr.append(dumpValue(child));
            break;
        default:
            break;
        }
    }
    return json_arr;
}

QVariant JsonTreeModel::dumpValue(JsonTreeItem *&item) const
{
    //QVariant对应QJsonValue
    return item->value();
}

对于TreeView的操作,我主要参考的官方示例: 

每个节点由TreeItem类构成,相当于链表的一个节点,而每个节点有一个父节点和多个子节点。需要设计的就是对于每个节点,需要标记他的类型,是array、object还是value,这样便于和JSON文档转换。

待改进的地方:除了完善增删节点的功能,还需要使用delegate委托一个ComboBox来编辑节点类型是array、object还是value。

代码链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/QJsonAndTreeView

5.参考

Qt文档:https://doc.qt.io/qt-5/json.html

讨论:https://forum.qt.io/topic/49771/jsonmodel-for-qtreeview

别人的实现:https://github.com/anjinkristou/QJsonTreeWidget

别人关于Model的:https://github.com/benlau/qsyncable

QML felgo框架的:https://felgo.com/apps/avoid-cpp-models-qt

博客QJson:http://blog.sina.com.cn/s/blog_a6fb6cc90101gnxm.html

博客QJson:https://www.cnblogs.com/lifan3a/articles/7811434.html

  • 15
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值