QJsonModel是基于QAbstractItemModel的json树模型类

qjsonmodel.h

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2011 SCHUTZ Sacha
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef QJSONMODEL_H
#define QJSONMODEL_H

#include <QAbstractItemModel>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QIcon>

namespace QUtf8Functions
{
    /// returns 0 on success; errors can only happen if \a u is a surrogate:
    /// Error if \a u is a low surrogate;
    /// if \a u is a high surrogate, Error if the next isn't a low one,
    /// EndOfString if we run into the end of the string.
    template <typename Traits, typename OutputPtr, typename InputPtr> inline
    int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end)
    {
        if (!Traits::skipAsciiHandling && u < 0x80)
        {
            // U+0000 to U+007F (US-ASCII) - one byte
            Traits::appendByte(dst, uchar(u));
            return 0;
        }
        else if (u < 0x0800)
        {
            // U+0080 to U+07FF - two bytes
            // first of two bytes
            Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
        }
        else
        {
            if (!QChar::isSurrogate(u))
            {
                // U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
                if (!Traits::allowNonCharacters && QChar::isNonCharacter(u))
                    return Traits::Error;
                // first of three bytes
                Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
            }
            else
            {
                // U+10000 to U+10FFFF - four bytes
                // need to get one extra codepoint
                if (Traits::availableUtf16(src, end) == 0)
                    return Traits::EndOfString;
                ushort low = Traits::peekUtf16(src);
                if (!QChar::isHighSurrogate(u))
                    return Traits::Error;
                if (!QChar::isLowSurrogate(low))
                    return Traits::Error;
                Traits::advanceUtf16(src);
                uint ucs4 = QChar::surrogateToUcs4(u, low);
                if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4))
                    return Traits::Error;
                // first byte
                Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf));
                // second of four bytes
                Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f));
                // for the rest of the bytes
                u = ushort(ucs4);
            }
            // second to last byte
            Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
        }
        // last byte
        Traits::appendByte(dst, 0x80 | (u & 0x3f));
        return 0;
    }
    inline bool isContinuationByte(uchar b)
    {
        return (b & 0xc0) == 0x80;
    }
    /// returns the number of characters consumed (including \a b) in case of success;
    /// returns negative in case of error: Traits::Error or Traits::EndOfString
    template <typename Traits, typename OutputPtr, typename InputPtr> inline
    int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end)
    {
        int charsNeeded;
        uint min_uc;
        uint uc;
        if (!Traits::skipAsciiHandling && b < 0x80)
        {
            // US-ASCII
            Traits::appendUtf16(dst, b);
            return 1;
        }
        if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1))
        {
            // an UTF-8 first character must be at least 0xC0
            // however, all 0xC0 and 0xC1 first bytes can only produce overlong sequences
            return Traits::Error;
        }
        else if (b < 0xe0)
        {
            charsNeeded = 2;
            min_uc = 0x80;
            uc = b & 0x1f;
        }
        else if (b < 0xf0)
        {
            charsNeeded = 3;
            min_uc = 0x800;
            uc = b & 0x0f;
        }
        else if (b < 0xf5)
        {
            charsNeeded = 4;
            min_uc = 0x10000;
            uc = b & 0x07;
        }
        else
        {
            // the last Unicode character is U+10FFFF
            // it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
            // therefore, a byte higher than 0xF4 is not the UTF-8 first byte
            return Traits::Error;
        }
        int bytesAvailable = Traits::availableBytes(src, end);
        if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1))
        {
            // it's possible that we have an error instead of just unfinished bytes
            if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0)))
                return Traits::Error;
            if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1)))
                return Traits::Error;
            return Traits::EndOfString;
        }
        // first continuation character
        b = Traits::peekByte(src, 0);
        if (!isContinuationByte(b))
            return Traits::Error;
        uc <<= 6;
        uc |= b & 0x3f;
        if (charsNeeded > 2)
        {
            // second continuation character
            b = Traits::peekByte(src, 1);
            if (!isContinuationByte(b))
                return Traits::Error;
            uc <<= 6;
            uc |= b & 0x3f;
            if (charsNeeded > 3)
            {
                // third continuation character
                b = Traits::peekByte(src, 2);
                if (!isContinuationByte(b))
                    return Traits::Error;
                uc <<= 6;
                uc |= b & 0x3f;
            }
        }
        // we've decoded something; safety-check it
        if (!Traits::isTrusted)
        {
            if (uc < min_uc)
                return Traits::Error;
            if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
                return Traits::Error;
            if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
                return Traits::Error;
        }
        // write the UTF-16 sequence
        if (!QChar::requiresSurrogates(uc))
        {
            // UTF-8 decoded and no surrogates are required
            // detach if necessary
            Traits::appendUtf16(dst, ushort(uc));
        }
        else
        {
            // UTF-8 decoded to something that requires a surrogate pair
            Traits::appendUcs4(dst, uc);
        }
        Traits::advanceByte(src, charsNeeded - 1);
        return charsNeeded;
    }
}

struct QUtf8BaseTraits
{
    static const bool isTrusted = false;
    static const bool allowNonCharacters = true;
    static const bool skipAsciiHandling = false;
    static const int Error = -1;
    static const int EndOfString = -2;
    static bool isValidCharacter(uint u)
    {
        return int(u) >= 0;
    }
    static void appendByte(uchar *&ptr, uchar b)
    {
        *ptr++ = b;
    }
    static uchar peekByte(const uchar *ptr, int n = 0)
    {
        return ptr[n];
    }
    static qptrdiff availableBytes(const uchar *ptr, const uchar *end)
    {
        return end - ptr;
    }
    static void advanceByte(const uchar *&ptr, int n = 1)
    {
        ptr += n;
    }
    static void appendUtf16(ushort *&ptr, ushort uc)
    {
        *ptr++ = uc;
    }
    static void appendUcs4(ushort *&ptr, uint uc)
    {
        appendUtf16(ptr, QChar::highSurrogate(uc));
        appendUtf16(ptr, QChar::lowSurrogate(uc));
    }
    static ushort peekUtf16(const ushort *ptr, int n = 0)
    {
        return ptr[n];
    }
    static qptrdiff availableUtf16(const ushort *ptr, const ushort *end)
    {
        return end - ptr;
    }
    static void advanceUtf16(const ushort *&ptr, int n = 1)
    {
        ptr += n;
    }
    // it's possible to output to UCS-4 too
    static void appendUtf16(uint *&ptr, ushort uc)
    {
        *ptr++ = uc;
    }
    static void appendUcs4(uint *&ptr, uint uc)
    {
        *ptr++ = uc;
    }
};

class QJsonModel;
class QJsonItem;

class QJsonTreeItem
{
public:
    QJsonTreeItem(QJsonTreeItem *parent = nullptr);
    ~QJsonTreeItem();
    void appendChild(QJsonTreeItem *item);
    QJsonTreeItem *child(int row);
    QJsonTreeItem *parent();
    int childCount() const;
    int row() const;
    void setKey(const QString& key);
    void setValue(const QVariant& value);
    void setType(const QJsonValue::Type& type);
    QString key() const;
    QVariant value() const;
    QJsonValue::Type type() const;

    static QJsonTreeItem *load(const QJsonValue& value, const QStringList &exceptions = {}, QJsonTreeItem *parent = nullptr);

protected:

private:
    QString mKey;
    QVariant mValue;
    QJsonValue::Type mType;
    QList<QJsonTreeItem*> mChilds;
    QJsonTreeItem *mParent = nullptr;
};

//---------------------------------------------------

class QJsonModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    explicit QJsonModel(QObject *parent = nullptr);
    QJsonModel(const QString& fileName, QObject *parent = nullptr);
    QJsonModel(QIODevice *device, QObject *parent = nullptr);
    QJsonModel(const QByteArray& json, QObject *parent = nullptr);
    ~QJsonModel();
    bool load(const QString& fileName);
    bool load(QIODevice *device);
    bool loadJson(const QByteArray& json);
    QVariant data(const QModelIndex &index, int role) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QByteArray json(bool compact = false);
    QByteArray jsonToByte(QJsonValue jsonValue);
    void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact);
    void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact);
    void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact);
    void objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact);
    void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, bool compact);
    //! List of tags to skip during JSON parsing
    void addException(const QStringList &exceptions);

private:
    QJsonValue genJson(QJsonTreeItem *) const;
    QJsonTreeItem *mRootItem = nullptr;
    QStringList mHeaders;
    //! List of exceptions (e.g. comments). Case insensitive, compairs on "contains".
    QStringList mExceptions;
};

#endif // QJSONMODEL_H

qjsonmodel.cpp

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2011 SCHUTZ Sacha
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "qjsonmodel.h"
#include <QDebug>
#include <QFile>
#include <QFont>


inline bool contains(const QStringList& list, const QString &value) {
    for (auto val : list)
        if (value.contains(val, Qt::CaseInsensitive))
            return true;

    return false;
}

QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent)
{
    mParent = parent;
}

QJsonTreeItem::~QJsonTreeItem()
{
    qDeleteAll(mChilds);
}

void QJsonTreeItem::appendChild(QJsonTreeItem *item)
{
    mChilds.append(item);
}

QJsonTreeItem *QJsonTreeItem::child(int row)
{
    return mChilds.value(row);
}

QJsonTreeItem *QJsonTreeItem::parent()
{
    return mParent;
}

int QJsonTreeItem::childCount() const
{
    return mChilds.count();
}

int QJsonTreeItem::row() const
{
    if (mParent)
        return mParent->mChilds.indexOf(const_cast<QJsonTreeItem*>(this));

    return 0;
}

void QJsonTreeItem::setKey(const QString &key)
{
    mKey = key;
}

void QJsonTreeItem::setValue(const QVariant &value)
{
    mValue = value;
}

void QJsonTreeItem::setType(const QJsonValue::Type &type)
{
    mType = type;
}

QString QJsonTreeItem::key() const
{
    return mKey;
}

QVariant QJsonTreeItem::value() const
{
    return mValue;
}

QJsonValue::Type QJsonTreeItem::type() const
{
    return mType;
}

QJsonTreeItem *QJsonTreeItem::load(const QJsonValue& value, const QStringList &exceptions, QJsonTreeItem *parent)
{
    QJsonTreeItem *rootItem = new QJsonTreeItem(parent);
    rootItem->setKey("root");

    if (value.isObject()) {
        //Get all QJsonValue childs
        const QStringList keys = value.toObject().keys(); // To prevent clazy-range warning
        for (const QString &key : keys) {
            if (contains(exceptions, key)) {
                continue;
            }
            QJsonValue v = value.toObject().value(key);
            QJsonTreeItem *child = load(v, exceptions, rootItem);
            child->setKey(key);
            child->setType(v.type());
            rootItem->appendChild(child);
        }
    } else if (value.isArray()) {
        //Get all QJsonValue childs
        int index = 0;
        const QJsonArray array = value.toArray(); // To prevent clazy-range warning
        for (const QJsonValue &v : array) {
            QJsonTreeItem *child = load(v, exceptions, rootItem);
            child->setKey(QString::number(index));
            child->setType(v.type());
            rootItem->appendChild(child);
            ++index;
        }
    } else {
        rootItem->setValue(value.toVariant());
        rootItem->setType(value.type());
    }

    return rootItem;
}

//=========================================================================

inline uchar hexdig(uint u)
{
    return (u < 0xa ? '0' + u : 'a' + u - 0xa);
}

QByteArray escapedString(const QString &s)
{
    QByteArray ba(s.length(), Qt::Uninitialized);
    uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
    const uchar *ba_end = cursor + ba.length();
    const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
    const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
    while (src != end) {
        if (cursor >= ba_end - 6) {
            // ensure we have enough space
            int pos = cursor - reinterpret_cast<const uchar *>(ba.constData());
            ba.resize(ba.size() * 2);
            cursor = reinterpret_cast<uchar *>(ba.data()) + pos;
            ba_end = reinterpret_cast<const uchar *>(ba.constData()) + ba.length();
        }
        uint u = *src++;
        if (u < 0x80) {
            if (u < 0x20 || u == 0x22 || u == 0x5c) {
                *cursor++ = '\\';
                switch (u) {
                case 0x22:
                    *cursor++ = '"';
                    break;
                case 0x5c:
                    *cursor++ = '\\';
                    break;
                case 0x8:
                    *cursor++ = 'b';
                    break;
                case 0xc:
                    *cursor++ = 'f';
                    break;
                case 0xa:
                    *cursor++ = 'n';
                    break;
                case 0xd:
                    *cursor++ = 'r';
                    break;
                case 0x9:
                    *cursor++ = 't';
                    break;
                default:
                    *cursor++ = 'u';
                    *cursor++ = '0';
                    *cursor++ = '0';
                    *cursor++ = hexdig(u >> 4);
                    *cursor++ = hexdig(u & 0xf);
                }
            } else {
                *cursor++ = (uchar)u;
            }
        } else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0) {
            // failed to get valid utf8 use JSON escape sequence
            *cursor++ = '\\';
            *cursor++ = 'u';
            *cursor++ = hexdig(u >> 12 & 0x0f);
            *cursor++ = hexdig(u >> 8 & 0x0f);
            *cursor++ = hexdig(u >> 4 & 0x0f);
            *cursor++ = hexdig(u & 0x0f);
        }
    }
    ba.resize(cursor - reinterpret_cast<const uchar *>(ba.constData()));
    return ba;
}

QJsonModel::QJsonModel(QObject *parent)
    : QAbstractItemModel(parent)
    , mRootItem{new QJsonTreeItem}
{
    mHeaders.append("key");
    mHeaders.append("value");
}

QJsonModel::QJsonModel(const QString& fileName, QObject *parent)
    : QAbstractItemModel(parent)
    , mRootItem{new QJsonTreeItem}
{
    mHeaders.append("key");
    mHeaders.append("value");
    load(fileName);
}

QJsonModel::QJsonModel(QIODevice *device, QObject *parent)
    : QAbstractItemModel(parent)
    , mRootItem{new QJsonTreeItem}
{
    mHeaders.append("key");
    mHeaders.append("value");
    load(device);
}

QJsonModel::QJsonModel(const QByteArray& json, QObject *parent)
    : QAbstractItemModel(parent)
    , mRootItem{new QJsonTreeItem}
{
    mHeaders.append("key");
    mHeaders.append("value");
    loadJson(json);
}

QJsonModel::~QJsonModel()
{
    delete mRootItem;
}

bool QJsonModel::load(const QString &fileName)
{
    QFile file(fileName);
    bool success = false;
    if (file.open(QIODevice::ReadOnly)) {
        success = load(&file);
        file.close();
    } else {
        success = false;
    }

    return success;
}

bool QJsonModel::load(QIODevice *device)
{
    return loadJson(device->readAll());
}

bool QJsonModel::loadJson(const QByteArray &json)
{
    auto const& jdoc = QJsonDocument::fromJson(json);

    if (!jdoc.isNull()) {
        beginResetModel();
        delete mRootItem;
        if (jdoc.isArray()) {
            mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array()), mExceptions);
            mRootItem->setType(QJsonValue::Array);

        } else {
            mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object()), mExceptions);
            mRootItem->setType(QJsonValue::Object);
        }
        endResetModel();
        return true;
    }

    qDebug()<<Q_FUNC_INFO<<"cannot load json";
    return false;
}


QVariant QJsonModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return {};

    QJsonTreeItem *item = static_cast<QJsonTreeItem*>(index.internalPointer());

    if (role == Qt::DisplayRole) {
        if (index.column() == 0)
            return QString("%1").arg(item->key());

        if (index.column() == 1)
            return item->value();
    } else if (Qt::EditRole == role) {
        if (index.column() == 1)
            return item->value();
    }

    return {};
}

bool QJsonModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    int col = index.column();
    if (Qt::EditRole == role) {
        if (col == 1) {
            QJsonTreeItem *item = static_cast<QJsonTreeItem*>(index.internalPointer());
            item->setValue(value);
            emit dataChanged(index, index, {Qt::EditRole});
            return true;
        }
    }

    return false;
}

QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return {};

    if (orientation == Qt::Horizontal)
        return mHeaders.value(section);
    else
        return {};
}

QModelIndex QJsonModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return {};

    QJsonTreeItem *parentItem;

    if (!parent.isValid())
        parentItem = mRootItem;
    else
        parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());

    QJsonTreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return {};
}

QModelIndex QJsonModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return {};

    QJsonTreeItem *childItem = static_cast<QJsonTreeItem*>(index.internalPointer());
    QJsonTreeItem *parentItem = childItem->parent();

    if (parentItem == mRootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int QJsonModel::rowCount(const QModelIndex &parent) const
{
    QJsonTreeItem *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = mRootItem;
    else
        parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());

    return parentItem->childCount();
}

int QJsonModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return 2;
}

Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const
{
    int col   = index.column();
    auto item = static_cast<QJsonTreeItem*>(index.internalPointer());

    auto isArray = QJsonValue::Array == item->type();
    auto isObject = QJsonValue::Object == item->type();

    if ((col == 1) && !(isArray || isObject))
        return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
    else
        return QAbstractItemModel::flags(index);
}

QByteArray QJsonModel::json(bool compact)
{
    auto jsonValue = genJson(mRootItem);
    QByteArray json;
    if (jsonValue.isNull())
        return json;

    if (jsonValue.isArray())
        arrayToJson(jsonValue.toArray(), json, 0, compact);
    else
        objectToJson(jsonValue.toObject(), json, 0, compact);

    return json;
}

void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact)
{
    json += compact ? "{" : "{\n";
    objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact);
    json += QByteArray(4 * indent, ' ');
    json += compact ? "}" : "}\n";
}
void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact)
{
    json += compact ? "[" : "[\n";
    arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
    json += QByteArray(4 * indent, ' ');
    json += compact ? "]" : "]\n";
}

void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact)
{
    if (jsonArray.size() <= 0)
        return;

    QByteArray indentString(4 * indent, ' ');
    int i = 0;
    while (1) {
        json += indentString;
        valueToJson(jsonArray.at(i), json, indent, compact);
        if (++i == jsonArray.size()) {
            if (!compact)
                json += '\n';
            break;
        }
        json += compact ? "," : ",\n";
    }
}
void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact)
{
    if (jsonObject.size() <= 0)
        return;

    QByteArray indentString(4 * indent, ' ');
    int i = 0;
    while (1) {
        QString key = jsonObject.keys().at(i);
        json += indentString;
        json += '"';
        json += escapedString(key);
        json += compact ? "\":" : "\": ";
        valueToJson(jsonObject.value(key), json, indent, compact);
        if (++i == jsonObject.size()) {
            if (!compact)
                json += '\n';
            break;
        }
        json += compact ? "," : ",\n";
    }
}

void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, bool compact)
{
    QJsonValue::Type type = jsonValue.type();
    switch (type) {
    case QJsonValue::Bool:
        json += jsonValue.toBool() ? "true" : "false";
        break;
    case QJsonValue::Double: {
        const double d = jsonValue.toDouble();
        if (qIsFinite(d)) {
            json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest);
        } else {
            json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
        }
        break;
    }
    case QJsonValue::String:
        json += '"';
        json += escapedString(jsonValue.toString());
        json += '"';
        break;
    case QJsonValue::Array:
        json += compact ? "[" : "[\n";
        arrayContentToJson(jsonValue.toArray(), json, indent + (compact ? 0 : 1), compact);
        json += QByteArray(4 * indent, ' ');
        json += ']';
        break;
    case QJsonValue::Object:
        json += compact ? "{" : "{\n";
        objectContentToJson(jsonValue.toObject(), json, indent + (compact ? 0 : 1), compact);
        json += QByteArray(4 * indent, ' ');
        json += '}';
        break;
    case QJsonValue::Null:
    default:
        json += "null";
    }
}

void QJsonModel::addException(const QStringList &exceptions)
{
    mExceptions = exceptions;
}

QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const
{
    auto type   = item->type();
    int  nchild = item->childCount();

    if (QJsonValue::Object == type) {
        QJsonObject jo;
        for (int i = 0; i < nchild; ++i) {
            auto ch = item->child(i);
            auto key = ch->key();
            jo.insert(key, genJson(ch));
        }
        return  jo;
    } else if (QJsonValue::Array == type) {
        QJsonArray arr;
        for (int i = 0; i < nchild; ++i) {
            auto ch = item->child(i);
            arr.append(genJson(ch));
        }
        return arr;
    } else {
        QJsonValue va;
        switch(item->value().type()) {
        case QVariant::Bool: {
            va = item->value().toBool();
            break;
        }
        default:
            va = item->value().toString();
            break;
        }
        (item->value());
        return va;
    }
}

使用 main.cpp

/***********************************************
    Copyright (C) 2014  Schutz Sacha
    This file is part of QJsonModel (https://github.com/dridk/QJsonmodel).

    QJsonModel is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QJsonModel is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QJsonModel.  If not, see <http://www.gnu.org/licenses/>.

**********************************************/

#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QTreeView>
#include <string>
#include "qjsonmodel.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTreeView *view   = new QTreeView;
    QJsonModel *model = new QJsonModel;

    view->setModel(model);

    std::string json = R"({
                       "firstName": "John",
                       "lastName": "Smith",
                       "age": 25,
                       "far-sighted": true,
                       "address":
                       {
                           "streetAddress": "21 2nd Street",
                           "city": "New York",
                           "state": "NY",
                           "postalCode": "10021"
                       },
                       "phoneNumber":
                       [
                           {
                             "comment": "This is just a comment!",
                             "type": "home",
                             "number": "212 555-1234"
                           },
                           {
                             "comment1": "Another comment!",
                             "type": "fax",
                             "number": "646 555-4567"
                           }
                       ]
                   })";

    model->addException({"comment"});
    model->loadJson(QByteArray::fromStdString(json));
    view->show();

    QByteArray mjson = model->json();
    qDebug() << mjson;

    return a.exec();
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要自定义一个QAbstractItemModel实现一个表格模型,可以按照以下步骤进行: 1. 继承QAbstractItemModel ```c++ class MyTableModel : public QAbstractItemModel { Q_OBJECT public: explicit MyTableModel(QObject *parent = nullptr); // 实现必要的虚函数 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; ``` 2. 实现虚函数 - index(): 根据行和列号以及父项索引返回对应的ModelIndex对象。 - parent(): 根据给定的子项索引返回父项的ModelIndex对象。 - rowCount(): 返回指定父项下的行数。 - columnCount(): 返回指定父项下的列数。 - data(): 返回模型索引对应的数据,根据不同的role返回不同的数据型。 - headerData(): 返回模型的表头数据,role参数表示请求的数据型。 3. 添加成员变量 在中添加需要用到的成员变量,如数据列表等。 4. 实现自定义函数 自定义一些函数,如添加、删除数据等。 5. 在界面中使用 在界面中使用自定义模型,例如: ```c++ MyTableModel* model = new MyTableModel(); QTableView* tableView = new QTableView(this); tableView->setModel(model); ``` 这样就可以在表格中显示自定义的数据了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值