Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsjsonutils.h
3 : : -------------
4 : : Date : May 206
5 : : Copyright : (C) 2016 Nyall Dawson
6 : : Email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgsjsonutils.h"
17 : : #include "qgsfeatureiterator.h"
18 : : #include "qgsogrutils.h"
19 : : #include "qgsgeometry.h"
20 : : #include "qgsvectorlayer.h"
21 : : #include "qgsrelation.h"
22 : : #include "qgsrelationmanager.h"
23 : : #include "qgsproject.h"
24 : : #include "qgsexception.h"
25 : : #include "qgslogger.h"
26 : : #include "qgsfieldformatterregistry.h"
27 : : #include "qgsfieldformatter.h"
28 : : #include "qgsapplication.h"
29 : : #include "qgsfeatureid.h"
30 : :
31 : : #include <QJsonDocument>
32 : : #include <QJsonArray>
33 : : #include <QTextCodec>
34 : : #include <nlohmann/json.hpp>
35 : :
36 : 0 : QgsJsonExporter::QgsJsonExporter( QgsVectorLayer *vectorLayer, int precision )
37 : 0 : : mPrecision( precision )
38 : 0 : , mLayer( vectorLayer )
39 : : {
40 : 0 : if ( vectorLayer )
41 : : {
42 : 0 : mCrs = vectorLayer->crs();
43 : 0 : mTransform.setSourceCrs( mCrs );
44 : 0 : }
45 : 0 : mTransform.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
46 : 0 : }
47 : :
48 : 0 : void QgsJsonExporter::setVectorLayer( QgsVectorLayer *vectorLayer )
49 : : {
50 : 0 : mLayer = vectorLayer;
51 : 0 : if ( vectorLayer )
52 : : {
53 : 0 : mCrs = vectorLayer->crs();
54 : 0 : mTransform.setSourceCrs( mCrs );
55 : 0 : }
56 : 0 : }
57 : :
58 : 0 : QgsVectorLayer *QgsJsonExporter::vectorLayer() const
59 : : {
60 : 0 : return mLayer.data();
61 : : }
62 : :
63 : 0 : void QgsJsonExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
64 : : {
65 : 0 : mCrs = crs;
66 : 0 : mTransform.setSourceCrs( mCrs );
67 : 0 : }
68 : :
69 : 0 : QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const
70 : : {
71 : 0 : return mCrs;
72 : : }
73 : :
74 : 0 : QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
75 : : const QVariant &id, int indent ) const
76 : : {
77 : 0 : return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump( indent ) );
78 : 0 : }
79 : :
80 : 0 : json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const
81 : : {
82 : 0 : json featureJson
83 : 0 : {
84 : 0 : { "type", "Feature" },
85 : : };
86 : 0 : if ( id.isValid() )
87 : : {
88 : 0 : bool ok = false;
89 : 0 : auto intId = id.toLongLong( &ok );
90 : 0 : if ( ok )
91 : : {
92 : 0 : featureJson["id"] = intId;
93 : 0 : }
94 : : else
95 : : {
96 : 0 : featureJson["id"] = id.toString().toStdString();
97 : : }
98 : 0 : }
99 : 0 : else if ( FID_IS_NULL( feature.id() ) )
100 : : {
101 : 0 : featureJson["id"] = nullptr;
102 : 0 : }
103 : : else
104 : : {
105 : 0 : featureJson["id"] = feature.id();
106 : : }
107 : :
108 : 0 : QgsGeometry geom = feature.geometry();
109 : 0 : if ( !geom.isNull() && mIncludeGeometry )
110 : : {
111 : 0 : if ( mCrs.isValid() )
112 : : {
113 : : try
114 : : {
115 : 0 : QgsGeometry transformed = geom;
116 : 0 : if ( mTransformGeometries && transformed.transform( mTransform ) == 0 )
117 : 0 : geom = transformed;
118 : 0 : }
119 : : catch ( QgsCsException &cse )
120 : : {
121 : 0 : Q_UNUSED( cse )
122 : 0 : }
123 : 0 : }
124 : 0 : QgsRectangle box = geom.boundingBox();
125 : :
126 : 0 : if ( QgsWkbTypes::flatType( geom.wkbType() ) != QgsWkbTypes::Point )
127 : : {
128 : 0 : featureJson[ "bbox" ] =
129 : 0 : {
130 : 0 : qgsRound( box.xMinimum(), mPrecision ),
131 : 0 : qgsRound( box.yMinimum(), mPrecision ),
132 : 0 : qgsRound( box.xMaximum(), mPrecision ),
133 : 0 : qgsRound( box.yMaximum(), mPrecision )
134 : : };
135 : 0 : }
136 : 0 : featureJson[ "geometry" ] = geom.asJsonObject( mPrecision );
137 : 0 : }
138 : : else
139 : : {
140 : 0 : featureJson[ "geometry" ] = nullptr;
141 : : }
142 : :
143 : : // build up properties element
144 : 0 : int attributeCounter { 0 };
145 : 0 : json properties;
146 : 0 : if ( mIncludeAttributes || !extraProperties.isEmpty() )
147 : : {
148 : : //read all attribute values from the feature
149 : 0 : if ( mIncludeAttributes )
150 : : {
151 : 0 : QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
152 : : // List of formatters through we want to pass the values
153 : 0 : QStringList formattersAllowList;
154 : 0 : formattersAllowList << QStringLiteral( "KeyValue" )
155 : 0 : << QStringLiteral( "List" )
156 : 0 : << QStringLiteral( "ValueRelation" )
157 : 0 : << QStringLiteral( "ValueMap" );
158 : :
159 : 0 : for ( int i = 0; i < fields.count(); ++i )
160 : : {
161 : 0 : if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
162 : 0 : continue;
163 : :
164 : 0 : QVariant val = feature.attributes().at( i );
165 : :
166 : 0 : if ( mLayer )
167 : : {
168 : 0 : const QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
169 : 0 : const QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
170 : 0 : if ( formattersAllowList.contains( fieldFormatter->id() ) )
171 : 0 : val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
172 : 0 : }
173 : :
174 : 0 : QString name = fields.at( i ).name();
175 : 0 : if ( mAttributeDisplayName )
176 : : {
177 : 0 : name = mLayer->attributeDisplayName( i );
178 : 0 : }
179 : 0 : properties[ name.toStdString() ] = QgsJsonUtils::jsonFromVariant( val );
180 : 0 : attributeCounter++;
181 : 0 : }
182 : 0 : }
183 : :
184 : 0 : if ( !extraProperties.isEmpty() )
185 : : {
186 : 0 : QVariantMap::const_iterator it = extraProperties.constBegin();
187 : 0 : for ( ; it != extraProperties.constEnd(); ++it )
188 : : {
189 : 0 : properties[ it.key().toStdString() ] = QgsJsonUtils::jsonFromVariant( it.value() );
190 : 0 : attributeCounter++;
191 : 0 : }
192 : 0 : }
193 : :
194 : : // related attributes
195 : 0 : if ( mLayer && mIncludeRelatedAttributes )
196 : : {
197 : 0 : QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
198 : 0 : for ( const auto &relation : std::as_const( relations ) )
199 : : {
200 : 0 : QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
201 : 0 : req.setFlags( QgsFeatureRequest::NoGeometry );
202 : 0 : QgsVectorLayer *childLayer = relation.referencingLayer();
203 : 0 : json relatedFeatureAttributes;
204 : 0 : if ( childLayer )
205 : : {
206 : 0 : QgsFeatureIterator it = childLayer->getFeatures( req );
207 : 0 : QVector<QVariant> attributeWidgetCaches;
208 : 0 : int fieldIndex = 0;
209 : 0 : const QgsFields fields { childLayer->fields() };
210 : 0 : for ( const QgsField &field : fields )
211 : : {
212 : 0 : QgsEditorWidgetSetup setup = field.editorWidgetSetup();
213 : 0 : QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
214 : 0 : attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
215 : 0 : fieldIndex++;
216 : 0 : }
217 : 0 : QgsFeature relatedFet;
218 : 0 : while ( it.nextFeature( relatedFet ) )
219 : : {
220 : 0 : relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches );
221 : : }
222 : 0 : }
223 : 0 : properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
224 : 0 : attributeCounter++;
225 : 0 : }
226 : 0 : }
227 : 0 : }
228 : 0 : featureJson[ "properties" ] = properties;
229 : 0 : return featureJson;
230 : 0 : }
231 : :
232 : 0 : QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
233 : : {
234 : 0 : return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
235 : 0 : }
236 : :
237 : 0 : json QgsJsonExporter::exportFeaturesToJsonObject( const QgsFeatureList &features ) const
238 : : {
239 : 0 : json data
240 : 0 : {
241 : 0 : { "type", "FeatureCollection" },
242 : 0 : { "features", json::array() }
243 : : };
244 : 0 : for ( const QgsFeature &feature : std::as_const( features ) )
245 : : {
246 : 0 : data["features"].push_back( exportFeatureToJsonObject( feature ) );
247 : : }
248 : 0 : return data;
249 : 0 : }
250 : :
251 : : //
252 : : // QgsJsonUtils
253 : : //
254 : :
255 : 0 : QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
256 : : {
257 : 0 : if ( !encoding )
258 : 0 : encoding = QTextCodec::codecForName( "UTF-8" );
259 : :
260 : 0 : return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
261 : : }
262 : :
263 : 0 : QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
264 : 0 : {
265 : 0 : if ( !encoding )
266 : 0 : encoding = QTextCodec::codecForName( "UTF-8" );
267 : 0 :
268 : 0 : return QgsOgrUtils::stringToFields( string, encoding );
269 : : }
270 : 0 :
271 : 0 : QString QgsJsonUtils::encodeValue( const QVariant &value )
272 : : {
273 : 0 : if ( value.isNull() )
274 : 0 : return QStringLiteral( "null" );
275 : :
276 : 0 : switch ( value.type() )
277 : : {
278 : : case QVariant::Int:
279 : 0 : case QVariant::UInt:
280 : : case QVariant::LongLong:
281 : 0 : case QVariant::ULongLong:
282 : : case QVariant::Double:
283 : 0 : return value.toString();
284 : :
285 : : case QVariant::Bool:
286 : 0 : return value.toBool() ? "true" : "false";
287 : :
288 : : case QVariant::StringList:
289 : : case QVariant::List:
290 : : case QVariant::Map:
291 : 0 : return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
292 : :
293 : : default:
294 : : case QVariant::String:
295 : 0 : QString v = value.toString()
296 : 0 : .replace( '\\', QLatin1String( "\\\\" ) )
297 : 0 : .replace( '"', QLatin1String( "\\\"" ) )
298 : 0 : .replace( '\r', QLatin1String( "\\r" ) )
299 : 0 : .replace( '\b', QLatin1String( "\\b" ) )
300 : 0 : .replace( '\t', QLatin1String( "\\t" ) )
301 : 0 : .replace( '/', QLatin1String( "\\/" ) )
302 : 0 : .replace( '\n', QLatin1String( "\\n" ) );
303 : :
304 : 0 : return v.prepend( '"' ).append( '"' );
305 : 0 : }
306 : 0 : }
307 : :
308 : 0 : QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
309 : : {
310 : 0 : QgsFields fields = feature.fields();
311 : 0 : QString attrs;
312 : 0 : for ( int i = 0; i < fields.count(); ++i )
313 : : {
314 : 0 : if ( i > 0 )
315 : 0 : attrs += QLatin1String( ",\n" );
316 : :
317 : 0 : QVariant val = feature.attributes().at( i );
318 : :
319 : 0 : if ( layer )
320 : : {
321 : 0 : QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
322 : 0 : QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
323 : 0 : if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
324 : 0 : val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
325 : 0 : }
326 : :
327 : 0 : attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
328 : 0 : }
329 : 0 : return attrs.prepend( '{' ).append( '}' );
330 : 0 : }
331 : :
332 : 0 : QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
333 : : {
334 : 0 : QString errorMessage;
335 : 0 : QVariantList result;
336 : : try
337 : : {
338 : 0 : const auto jObj( json::parse( json.toStdString() ) );
339 : 0 : if ( ! jObj.is_array() )
340 : : {
341 : 0 : throw json::parse_error::create( 0, 0, QStringLiteral( "JSON value must be an array" ).toStdString() );
342 : : }
343 : 0 : for ( const auto &item : jObj )
344 : : {
345 : : // Create a QVariant from the array item
346 : 0 : QVariant v;
347 : 0 : if ( item.is_number_integer() )
348 : : {
349 : 0 : v = item.get<int>();
350 : 0 : }
351 : 0 : else if ( item.is_number_unsigned() )
352 : : {
353 : 0 : v = item.get<unsigned>();
354 : 0 : }
355 : 0 : else if ( item.is_number_float() )
356 : : {
357 : : // Note: it's a double and not a float on purpose
358 : 0 : v = item.get<double>();
359 : 0 : }
360 : 0 : else if ( item.is_string() )
361 : : {
362 : 0 : v = QString::fromStdString( item.get<std::string>() );
363 : 0 : }
364 : 0 : else if ( item.is_boolean() )
365 : : {
366 : 0 : v = item.get<bool>();
367 : 0 : }
368 : 0 : else if ( item.is_null() )
369 : : {
370 : : // Fallback to int
371 : 0 : v = QVariant( type == QVariant::Type::Invalid ? QVariant::Type::Int : type );
372 : 0 : }
373 : :
374 : : // If a destination type was specified (it's not invalid), try to convert
375 : 0 : if ( type != QVariant::Invalid )
376 : : {
377 : 0 : if ( ! v.convert( static_cast<int>( type ) ) )
378 : : {
379 : 0 : QgsLogger::warning( QStringLiteral( "Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
380 : 0 : }
381 : : else
382 : : {
383 : 0 : result.push_back( v );
384 : : }
385 : 0 : }
386 : : else
387 : : {
388 : 0 : result.push_back( v );
389 : : }
390 : 0 : }
391 : 0 : }
392 : : catch ( json::parse_error &ex )
393 : : {
394 : 0 : errorMessage = ex.what();
395 : 0 : QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
396 : 0 : }
397 : :
398 : 0 : return result;
399 : 0 : }
400 : :
401 : 0 : json QgsJsonUtils::jsonFromVariant( const QVariant &val )
402 : : {
403 : 0 : if ( val.isNull() || ! val.isValid() )
404 : : {
405 : 0 : return nullptr;
406 : : }
407 : 0 : json j;
408 : 0 : if ( val.type() == QVariant::Type::Map )
409 : : {
410 : 0 : const QVariantMap &vMap = val.toMap();
411 : 0 : json jMap = json::object();
412 : 0 : for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
413 : : {
414 : 0 : jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() );
415 : 0 : }
416 : 0 : j = jMap;
417 : 0 : }
418 : 0 : else if ( val.type() == QVariant::Type::List || val.type() == QVariant::Type::StringList )
419 : : {
420 : 0 : const QVariantList &vList = val.toList();
421 : 0 : json jList = json::array();
422 : 0 : for ( const auto &v : vList )
423 : : {
424 : 0 : jList.push_back( jsonFromVariant( v ) );
425 : : }
426 : 0 : j = jList;
427 : 0 : }
428 : : else
429 : : {
430 : 0 : switch ( val.userType() )
431 : : {
432 : : case QMetaType::Int:
433 : : case QMetaType::UInt:
434 : : case QMetaType::LongLong:
435 : : case QMetaType::ULongLong:
436 : 0 : j = val.toLongLong();
437 : 0 : break;
438 : : case QMetaType::Double:
439 : : case QMetaType::Float:
440 : 0 : j = val.toDouble();
441 : 0 : break;
442 : : case QMetaType::Bool:
443 : 0 : j = val.toBool();
444 : 0 : break;
445 : : case QMetaType::QByteArray:
446 : 0 : j = val.toByteArray().toBase64().toStdString();
447 : 0 : break;
448 : : default:
449 : 0 : j = val.toString().toStdString();
450 : 0 : break;
451 : : }
452 : : }
453 : 0 : return j;
454 : 0 : }
455 : :
456 : 0 : QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
457 : : {
458 : : // tracks whether entire json string is a primitive
459 : 0 : bool isPrimitive = true;
460 : :
461 : 0 : std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
462 : 0 : QVariant result;
463 : 0 : if ( jObj.is_array() )
464 : : {
465 : 0 : isPrimitive = false;
466 : 0 : QVariantList results;
467 : 0 : for ( const auto &item : jObj )
468 : : {
469 : 0 : results.push_back( _parser( item ) );
470 : : }
471 : 0 : result = results;
472 : 0 : }
473 : 0 : else if ( jObj.is_object() )
474 : : {
475 : 0 : isPrimitive = false;
476 : 0 : QVariantMap results;
477 : 0 : for ( const auto &item : jObj.items() )
478 : : {
479 : 0 : const auto key { QString::fromStdString( item.key() ) };
480 : 0 : const auto value { _parser( item.value() ) };
481 : 0 : results[ key ] = value;
482 : 0 : }
483 : 0 : result = results;
484 : 0 : }
485 : : else
486 : : {
487 : 0 : if ( jObj.is_number_integer() )
488 : : {
489 : 0 : result = jObj.get<int>();
490 : 0 : }
491 : 0 : else if ( jObj.is_number_unsigned() )
492 : : {
493 : 0 : result = jObj.get<unsigned>();
494 : 0 : }
495 : 0 : else if ( jObj.is_boolean() )
496 : : {
497 : 0 : result = jObj.get<bool>();
498 : 0 : }
499 : 0 : else if ( jObj.is_number_float() )
500 : : {
501 : : // Note: it's a double and not a float on purpose
502 : 0 : result = jObj.get<double>();
503 : 0 : }
504 : 0 : else if ( jObj.is_string() )
505 : : {
506 : 0 : if ( isPrimitive && jObj.get<std::string>().length() == 0 )
507 : : {
508 : 0 : result = QString::fromStdString( jObj.get<std::string>() ).append( "\"" ).insert( 0, "\"" );
509 : 0 : }
510 : : else
511 : : {
512 : 0 : result = QString::fromStdString( jObj.get<std::string>() );
513 : : }
514 : 0 : }
515 : 0 : else if ( jObj.is_null() )
516 : : {
517 : : // Do nothing (leave invalid)
518 : 0 : }
519 : : }
520 : 0 : return result;
521 : 0 : }
522 : : };
523 : :
524 : : try
525 : : {
526 : 0 : const json j = json::parse( jsonString );
527 : 0 : return _parser( j );
528 : 0 : }
529 : : catch ( json::parse_error &ex )
530 : : {
531 : 0 : QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( QString::fromStdString( ex.what() ),
532 : 0 : QString::fromStdString( jsonString ) ) );
533 : 0 : }
534 : 0 : return QVariant();
535 : 0 : }
536 : :
537 : 0 : QVariant QgsJsonUtils::parseJson( const QString &jsonString )
538 : : {
539 : 0 : return parseJson( jsonString.toStdString() );
540 : 0 : }
541 : :
542 : 0 : json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
543 : : {
544 : 0 : QgsFields fields = feature.fields();
545 : 0 : json attrs;
546 : 0 : for ( int i = 0; i < fields.count(); ++i )
547 : : {
548 : 0 : QVariant val = feature.attributes().at( i );
549 : :
550 : 0 : if ( layer )
551 : : {
552 : 0 : QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
553 : 0 : QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
554 : 0 : if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
555 : 0 : val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
556 : 0 : }
557 : 0 : attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
558 : 0 : }
559 : 0 : return attrs;
560 : 0 : }
|