KPublicTransport

json.cpp
1 /*
2  SPDX-FileCopyrightText: 2018 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "json_p.h"
8 #include "logging.h"
9 
10 #include <QColor>
11 #include <QDateTime>
12 #include <QDebug>
13 #include <QLocale>
14 #include <QMetaObject>
15 #include <QMetaProperty>
16 #include <QRectF>
17 #include <QTimeZone>
18 #include <QUrl>
19 #include <QVariant>
20 
21 #include <cmath>
22 
23 using namespace KPublicTransport;
24 
25 QString Json::translatedValue(const QJsonObject &obj, const QString &key)
26 {
27  auto languageWithCountry = QLocale().name();
28  auto it = obj.constFind(key + QLatin1Char('[') + languageWithCountry + QLatin1Char(']'));
29  if (it != obj.constEnd()) {
30  return it.value().toString();
31  }
32  const auto language = QStringView(languageWithCountry).mid(0, languageWithCountry.indexOf(QLatin1Char('_')));
33  it = obj.constFind(key + QLatin1Char('[') + language + QLatin1Char(']'));
34  if (it != obj.constEnd()) {
35  return it.value().toString();
36  }
37  return obj.value(key).toString();
38 }
39 
40 QStringList Json::toStringList(const QJsonValue &v)
41 {
42  const auto a = v.toArray();
43  QStringList l;
44  l.reserve(a.size());
45  for (const auto &av : a) {
46  l.push_back(av.toString());
47  }
48  return l;
49 }
50 
51 static QJsonValue variantToJson(const QVariant &v)
52 {
53  switch (v.userType()) {
54  case QMetaType::QString:
55  {
56  const auto s = v.toString();
57  return s.isNull() ? QJsonValue() : v.toString();
58  }
59  case QMetaType::Double:
60  case QMetaType::Float:
61  {
62  auto d = v.toDouble();
63  if (std::isnan(d)) {
64  return QJsonValue::Null;
65  }
66  return d;
67  }
68  case QMetaType::Int:
69  return v.toInt();
70  case QVariant::DateTime:
71  {
72  const auto dt = v.toDateTime();
73  if (!dt.isValid()) {
74  return {};
75  }
76  if (dt.timeSpec() == Qt::TimeZone) {
77  QJsonObject dtObj;
78  dtObj.insert(QStringLiteral("value"), dt.toString(Qt::ISODate));
79  dtObj.insert(QStringLiteral("timezone"), QString::fromUtf8(dt.timeZone().id()));
80  return dtObj;
81  }
82  return v.toDateTime().toString(Qt::ISODate);
83  }
84  case QVariant::Url:
85  {
86  const auto url = v.toUrl();
87  return url.isValid() ? url.toString() : QJsonValue();
88  }
89  case QVariant::RectF:
90  {
91  const auto r = v.toRectF();
92  QJsonObject obj;
93  obj.insert(QStringLiteral("x1"), r.topLeft().x());
94  obj.insert(QStringLiteral("y1"), r.topLeft().y());
95  obj.insert(QStringLiteral("x2"), r.bottomRight().x());
96  obj.insert(QStringLiteral("y2"), r.bottomRight().y());
97  return obj;
98  }
99  }
100 
101  if (v.userType() == qMetaTypeId<QColor>()) {
102  const auto c = v.value<QColor>();
103  return c.isValid() ? v.value<QColor>().name() : QJsonValue();;
104  }
105 
106  if (v.canConvert<QVariantList>()) {
107  const auto l = v.toList();
108  if (l.isEmpty()) {
109  return {};
110  }
111 
112  QJsonArray a;
113  std::transform(l.begin(), l.end(), std::back_inserter(a), variantToJson);
114  return a;
115  }
116 
117  return {};
118 }
119 
120 QJsonObject Json::toJson(const QMetaObject *mo, const void *elem)
121 {
122  QJsonObject obj;
123 
124  for (int i = 0; i < mo->propertyCount(); ++i) {
125  const auto prop = mo->property(i);
126  if (!prop.isStored()) {
127  continue;
128  }
129 
130  if (prop.isFlagType()) { // flag has to come first, as prop.isEnumType() is also true for this
131  const auto key = prop.readOnGadget(elem).toInt();
132  const auto value = prop.enumerator().valueToKeys(key);
133  obj.insert(QString::fromUtf8(prop.name()), QString::fromUtf8(value));
134  continue;
135  }
136  if (prop.isEnumType()) { // enums defined in this QMO
137  const auto key = prop.readOnGadget(elem).toInt();
138  const auto value = prop.enumerator().valueToKey(key);
139  obj.insert(QString::fromUtf8(prop.name()), QString::fromUtf8(value));
140  continue;
141  } else if (QMetaType::typeFlags(prop.userType()) & QMetaType::IsEnumeration) { // external enums
142  obj.insert(QString::fromUtf8(prop.name()), prop.readOnGadget(elem).toString());
143  continue;
144  }
145 
146  const auto v = variantToJson(prop.readOnGadget(elem));
147  if (!v.isNull()) {
148  obj.insert(QString::fromUtf8(prop.name()), v);
149  }
150  }
151 
152  return obj;
153 }
154 
155 static QVariant variantFromJson(const QJsonValue &v, int mt)
156 {
157  switch (mt) {
158  case QMetaType::QString:
159  return v.toString();
160  case QMetaType::Double:
161  case QMetaType::Float:
162  return v.toDouble();
163  case QMetaType::Int:
164  return v.toInt();
165  case QVariant::DateTime:
166  {
167  if (v.isObject()) {
168  const auto dtObj = v.toObject();
169  auto dt = QDateTime::fromString(dtObj.value(QLatin1String("value")).toString(), Qt::ISODate);
170  dt.setTimeZone(QTimeZone(dtObj.value(QLatin1String("timezone")).toString().toUtf8()));
171  return dt;
172  }
174  }
175  case QVariant::Url:
176  return QUrl(v.toString());
178  return Json::toStringList(v);
179  case QVariant::RectF:
180  {
181  const auto obj = v.toObject();
182  QRectF r;
183  r.setTopLeft(QPointF(obj.value(QLatin1String("x1")).toDouble(), obj.value(QLatin1String("y1")).toDouble()));
185  return r;
186  }
187  }
188 
189  if (mt == qMetaTypeId<QColor>()) {
190  return QColor(v.toString());
191  }
192 
193  return {};
194 }
195 
196 void Json::fromJson(const QMetaObject *mo, const QJsonObject &obj, void *elem)
197 {
198  for (auto it = obj.begin(); it != obj.end(); ++it) {
199  const auto idx = mo->indexOfProperty(it.key().toUtf8().constData());
200  if (idx < 0) {
201  continue;
202  }
203 
204  const auto prop = mo->property(idx);
205  if (!prop.isStored()) {
206  continue;
207  }
208 
209  if (prop.isFlagType() && it.value().isString()) {
210  const auto key = prop.enumerator().keysToValue(it.value().toString().toUtf8().constData());
211  prop.writeOnGadget(elem, key);
212  continue;
213  }
214  if (prop.isEnumType() && it.value().isString()) { // internal enums in this QMO
215  const auto key = prop.enumerator().keyToValue(it.value().toString().toUtf8().constData());
216  prop.writeOnGadget(elem, key);
217  continue;
218  }
219  if ((QMetaType::typeFlags(prop.userType()) & QMetaType::IsEnumeration) && it.value().isString()) { // external enums
220  const QMetaType mt(prop.userType());
221  const auto mo = mt.metaObject();
222  if (!mo) {
223  qCWarning(Log) << "No meta object found for enum type:" << prop.type();
224  continue;
225  }
226  const auto enumIdx = mo->indexOfEnumerator(prop.typeName() + strlen(mo->className()) + 2);
227  if (enumIdx < 0) {
228  qCWarning(Log) << "Could not find QMetaEnum for" << prop.type();
229  continue;
230  }
231  const auto me = mo->enumerator(enumIdx);
232  bool success = false;
233  const auto numValue = me.keyToValue(it.value().toString().toUtf8().constData(), &success);
234  if (!success) {
235  qCWarning(Log) << "Unknown enum value" << it.value().toString() << "for" << prop.type();
236  continue;
237  }
238  auto valueData = mt.create();
239  *reinterpret_cast<int*>(valueData) = numValue;
240  QVariant value(prop.userType(), valueData);
241  prop.writeOnGadget(elem, value);
242  continue;
243  }
244 
245  const auto v = variantFromJson(it.value(), prop.userType());
246  prop.writeOnGadget(elem, v);
247  }
248 }
bool canConvert(int targetTypeId) const const
QUrl toUrl() const const
QString toString(Qt::DateFormat format) const const
Query operations and data types for accessing realtime public transport information from online servi...
Definition: attribution.cpp:16
int indexOfEnumerator(const char *name) const const
void push_back(const T &value)
QJsonObject::const_iterator constEnd() const const
QList< QVariant > toList() const const
void reserve(int alloc)
QDateTime toDateTime() const const
T value() const const
bool isObject() const const
QJsonObject::iterator end()
QJsonObject::iterator begin()
bool isNull() const const
TimeZone
int toInt(int defaultValue) const const
void setTimeZone(const QTimeZone &toZone)
QMetaType::TypeFlags typeFlags(int type)
int propertyCount() const const
QString toString() const const
QString fromUtf8(const char *str, int size)
int toInt(bool *ok) const const
bool isNull() const const
QJsonObject toObject() const const
int toInt(bool *ok, int base) const const
QJsonArray toArray() const const
bool isEmpty() const const
QString name() const const
int userType() const const
QList::iterator end()
QDateTime fromString(const QString &string, Qt::DateFormat format)
void setTopLeft(const QPointF &position)
int indexOfProperty(const char *name) const const
bool isValid() const const
char * toString(const T &value)
QJsonValue value(const QString &key) const const
double toDouble(bool *ok) const const
QRectF toRectF() const const
void setBottomRight(const QPointF &position)
QString toString() const const
QJsonObject::iterator insert(const QString &key, const QJsonValue &value)
QList::iterator begin()
QJsonObject::const_iterator constFind(const QString &key) const const
QStringView mid(qsizetype start) const const
double toDouble(double defaultValue) const const
QMetaProperty property(int index) const const
bool isValid() const const
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 23:05:21 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.