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();
}