Skip to content

Commit

Permalink
Qt JSON types support (#6)
Browse files Browse the repository at this point in the history
* Qt JSON types support

This adds support for Qt’s JSON types as proposed in the comment of PR #5.

The difficulty in using Qt’s JSON data types was that they can be
casted to QVariantList/QVariantHash if they are inside a QVariant and
e.g. `QVariant::canConvert<QVariantList>` returns true for a QJsonArray,
but the next step by Cutelee is to cast them into QAssociativeIterable
or QSequentialIterable. That only works for template based containers.

So, my solution is to “intercept“ this types before the QVariant cast is
tried and convert them directly into QVariantList or QVariantHash. If
the JSON type is direct part of the context, this happens in
`Cutelee::Variable::resolve()`, if the JSON type is stored in a context
variable’s property or further down the road, the lookup will be done in
`Cutelee::MetaType::lookup()`.

Unit tests are currently written to test “normal“ usage as variable and
to be used in a `{% for %}` loop tag. Maybe we should add some more tests
to see if they play nicely with filters and the rest, even though I
think that this implementations should make this possible without
further changes.

* Use QJsonValue::toVariant() in Variable::resolve()

Make the switch a bit simpler and convert directly to QVariant.

* Use QJson data types in JSON lookup functions
  • Loading branch information
buschmann23 authored May 21, 2020
1 parent 35e7dbf commit 2e7317d
Show file tree
Hide file tree
Showing 3 changed files with 471 additions and 1 deletion.
88 changes: 87 additions & 1 deletion templates/lib/metatype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include "metaenumvariable_p.h"

#include <QtCore/QDebug>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

using namespace Cutelee;

Expand Down Expand Up @@ -106,12 +110,94 @@ static QVariant doQobjectLookUp(const QObject *const object,
return object->property(property.toUtf8().constData());
}

static QVariant doJsonArrayLookUp(const QJsonArray &list,
const QString &property)
{
if (property == QLatin1String("count") || property == QLatin1String("size")) {
return list.size();
}

bool ok = false;
const int listIndex = property.toInt(&ok);
if (!ok || listIndex >= list.size()) {
return QVariant();
}

return list.at(listIndex).toVariant();
}

static QVariant doJsonObjectLookUp(const QJsonObject &obj,
const QString &property)
{
if (property == QLatin1String("count") || property == QLatin1String("size")) {
return obj.size();
}

if (property == QLatin1String("items")) {
QVariantList list;
list.reserve(obj.size());
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
list.push_back(QVariantList{it.key(), it.value().toVariant()});
}
return list;
}

if (property == QLatin1String("keys")) {
return obj.keys();
}

if (property == QLatin1String("values")) {
QVariantList list;
list.reserve(obj.size());
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
list.push_back(it.value().toVariant());
}
return list;
}

return obj.value(property).toVariant();
}

QVariant Cutelee::MetaType::lookup(const QVariant &object,
const QString &property)
const QString &property)
{
if (object.canConvert<QObject *>()) {
return doQobjectLookUp(object.value<QObject *>(), property);
}
if (object.userType() == QMetaType::QJsonDocument) {
const auto doc = object.toJsonDocument();
if (doc.isObject()) {
return doJsonObjectLookUp(doc.object(), property);
}
if (doc.isArray()) {
return doJsonArrayLookUp(doc.array(), property);
}
return QVariant();
}
if (object.userType() == QMetaType::QJsonValue) {
const auto val = object.toJsonValue();

switch (val.type()) {
case QJsonValue::Bool:
return val.toBool();
case QJsonValue::Double:
return val.toDouble();
case QJsonValue::String:
return val.toString();
case QJsonValue::Array:
return doJsonArrayLookUp(val.toArray(), property);
case QJsonValue::Object:
return doJsonObjectLookUp(val.toObject(), property);
default:
return QVariant();
}
}
if (object.userType() == QMetaType::QJsonArray) {
return doJsonArrayLookUp(object.toJsonArray(), property);
}
if (object.userType() == QMetaType::QJsonObject) {
return doJsonObjectLookUp(object.toJsonObject(), property);
}
if (object.canConvert<QVariantList>()) {
auto iter = object.value<QSequentialIterable>();
if (property == QStringLiteral("size")
Expand Down
38 changes: 38 additions & 0 deletions templates/lib/variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

#include <QtCore/QMetaEnum>
#include <QtCore/QStringList>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>

using namespace Cutelee;

Expand Down Expand Up @@ -206,6 +210,40 @@ QVariant Variable::resolve(Context *c) const

} else {
var = c->lookup(d->m_lookups.at(i++));
if (var.userType() == QMetaType::QJsonDocument) {
const auto jsonDoc = var.toJsonDocument();
if (jsonDoc.isArray()) {
var = jsonDoc.array().toVariantList();
} else if (jsonDoc.isObject()) {
var = jsonDoc.object().toVariantHash();
} else {
// JSON document is eather empty or null
return QVariant();
}
} else if (var.userType() == QMetaType::QJsonValue) {
const auto jsonVal = var.toJsonValue();
switch(jsonVal.type()) {
case QJsonValue::Bool:
var = jsonVal.toBool();
break;
case QJsonValue::Double:
var = jsonVal.toDouble();
break;
case QJsonValue::String:
var = jsonVal.toString();
break;
case QJsonValue::Array:
case QJsonValue::Object:
var = jsonVal.toVariant();
break;
default:
return QVariant();
}
} else if (var.userType() == QMetaType::QJsonArray) {
var = var.toJsonArray().toVariantList();
} else if (var.userType() == QMetaType::QJsonObject) {
var = var.toJsonObject().toVariantHash();
}
}
while (i < d->m_lookups.size()) {
var = MetaType::lookup(var, d->m_lookups.at(i++));
Expand Down
Loading

0 comments on commit 2e7317d

Please sign in to comment.