QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsjsonutils.cpp
Go to the documentation of this file.
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"
27 #include "qgsfieldformatter.h"
28 #include "qgsapplication.h"
29 
30 #include <QJsonDocument>
31 #include <QJsonArray>
32 #include <nlohmann/json.hpp>
33 
35  : mPrecision( precision )
36  , mLayer( vectorLayer )
37 {
38  if ( vectorLayer )
39  {
40  mCrs = vectorLayer->crs();
41  mTransform.setSourceCrs( mCrs );
42  }
44 }
45 
47 {
48  mLayer = vectorLayer;
49  if ( vectorLayer )
50  {
51  mCrs = vectorLayer->crs();
52  mTransform.setSourceCrs( mCrs );
53  }
54 }
55 
57 {
58  return mLayer.data();
59 }
60 
62 {
63  mCrs = crs;
64  mTransform.setSourceCrs( mCrs );
65 }
66 
68 {
69  return mCrs;
70 }
71 
72 QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
73  const QVariant &id, int indent ) const
74 {
75  return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump( indent ) );
76 }
77 
78 json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const
79 {
80  json featureJson
81  {
82  { "type", "Feature" },
83  };
84  if ( id.isValid() )
85  {
86  bool ok = false;
87  auto intId = id.toLongLong( &ok );
88  if ( ok )
89  {
90  featureJson["id"] = intId;
91  }
92  else
93  {
94  featureJson["id"] = id.toString().toStdString();
95  }
96  }
97  else
98  {
99  featureJson["id"] = feature.id();
100  }
101 
102  QgsGeometry geom = feature.geometry();
103  if ( !geom.isNull() && mIncludeGeometry )
104  {
105  if ( mCrs.isValid() )
106  {
107  try
108  {
109  QgsGeometry transformed = geom;
110  if ( transformed.transform( mTransform ) == 0 )
111  geom = transformed;
112  }
113  catch ( QgsCsException &cse )
114  {
115  Q_UNUSED( cse )
116  }
117  }
118  QgsRectangle box = geom.boundingBox();
119 
121  {
122  featureJson[ "bbox" ] =
123  {
124  qgsRound( box.xMinimum(), mPrecision ),
125  qgsRound( box.yMinimum(), mPrecision ),
126  qgsRound( box.xMaximum(), mPrecision ),
127  qgsRound( box.yMaximum(), mPrecision )
128  };
129  }
130  featureJson[ "geometry" ] = geom.asJsonObject( mPrecision );
131  }
132  else
133  {
134  featureJson[ "geometry" ] = nullptr;
135  }
136 
137  // build up properties element
138  int attributeCounter { 0 };
139  json properties;
140  if ( mIncludeAttributes || !extraProperties.isEmpty() )
141  {
142  //read all attribute values from the feature
143  if ( mIncludeAttributes )
144  {
145  QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
146  // List of formatters through we want to pass the values
147  QStringList formattersWhiteList;
148  formattersWhiteList << QStringLiteral( "KeyValue" )
149  << QStringLiteral( "List" )
150  << QStringLiteral( "ValueRelation" )
151  << QStringLiteral( "ValueMap" );
152 
153  for ( int i = 0; i < fields.count(); ++i )
154  {
155  if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
156  continue;
157 
158  QVariant val = feature.attributes().at( i );
159 
160  if ( mLayer )
161  {
162  const QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
163  const QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
164  if ( formattersWhiteList.contains( fieldFormatter->id() ) )
165  val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
166  }
167 
168  QString name = fields.at( i ).name();
169  if ( mAttributeDisplayName )
170  {
171  name = mLayer->attributeDisplayName( i );
172  }
173  properties[ name.toStdString() ] = QgsJsonUtils::jsonFromVariant( val );
174  attributeCounter++;
175  }
176  }
177 
178  if ( !extraProperties.isEmpty() )
179  {
180  QVariantMap::const_iterator it = extraProperties.constBegin();
181  for ( ; it != extraProperties.constEnd(); ++it )
182  {
183  properties[ it.key().toStdString() ] = QgsJsonUtils::jsonFromVariant( it.value() );
184  attributeCounter++;
185  }
186  }
187 
188  // related attributes
189  if ( mLayer && mIncludeRelatedAttributes )
190  {
191  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
192  for ( const auto &relation : qgis::as_const( relations ) )
193  {
194  QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
196  QgsVectorLayer *childLayer = relation.referencingLayer();
197  json relatedFeatureAttributes;
198  if ( childLayer )
199  {
200  QgsFeatureIterator it = childLayer->getFeatures( req );
201  QVector<QVariant> attributeWidgetCaches;
202  int fieldIndex = 0;
203  const QgsFields fields { childLayer->fields() };
204  for ( const QgsField &field : fields )
205  {
206  QgsEditorWidgetSetup setup = field.editorWidgetSetup();
208  attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
209  fieldIndex++;
210  }
211  QgsFeature relatedFet;
212  while ( it.nextFeature( relatedFet ) )
213  {
214  relatedFeatureAttributes += QgsJsonUtils::exportAttributesToJsonObject( relatedFet, childLayer, attributeWidgetCaches );
215  }
216  }
217  properties[ relation.name().toStdString() ] = relatedFeatureAttributes;
218  attributeCounter++;
219  }
220  }
221  }
222  featureJson[ "properties" ] = properties;
223  return featureJson;
224 }
225 
226 QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const
227 {
228  return QString::fromStdString( exportFeaturesToJsonObject( features ).dump( indent ) );
229 }
230 
232 {
233  json data
234  {
235  { "type", "FeatureCollection" },
236  { "features", json::array() }
237  };
238  for ( const QgsFeature &feature : qgis::as_const( features ) )
239  {
240  data["features"].push_back( exportFeatureToJsonObject( feature ) );
241  }
242  return data;
243 }
244 
245 //
246 // QgsJsonUtils
247 //
248 
249 QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
250 {
251  return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
252 }
253 
254 QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
255 {
256  return QgsOgrUtils::stringToFields( string, encoding );
257 }
258 
259 QString QgsJsonUtils::encodeValue( const QVariant &value )
260 {
261  if ( value.isNull() )
262  return QStringLiteral( "null" );
263 
264  switch ( value.type() )
265  {
266  case QVariant::Int:
267  case QVariant::UInt:
268  case QVariant::LongLong:
269  case QVariant::ULongLong:
270  case QVariant::Double:
271  return value.toString();
272 
273  case QVariant::Bool:
274  return value.toBool() ? "true" : "false";
275 
276  case QVariant::StringList:
277  case QVariant::List:
278  case QVariant::Map:
279  return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
280 
281  default:
282  case QVariant::String:
283  QString v = value.toString()
284  .replace( '\\', QLatin1String( "\\\\" ) )
285  .replace( '"', QLatin1String( "\\\"" ) )
286  .replace( '\r', QLatin1String( "\\r" ) )
287  .replace( '\b', QLatin1String( "\\b" ) )
288  .replace( '\t', QLatin1String( "\\t" ) )
289  .replace( '/', QLatin1String( "\\/" ) )
290  .replace( '\n', QLatin1String( "\\n" ) );
291 
292  return v.prepend( '"' ).append( '"' );
293  }
294 }
295 
296 QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
297 {
298  QgsFields fields = feature.fields();
299  QString attrs;
300  for ( int i = 0; i < fields.count(); ++i )
301  {
302  if ( i > 0 )
303  attrs += QLatin1String( ",\n" );
304 
305  QVariant val = feature.attributes().at( i );
306 
307  if ( layer )
308  {
309  QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
312  val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
313  }
314 
315  attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
316  }
317  return attrs.prepend( '{' ).append( '}' );
318 }
319 
320 QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
321 {
322  QString errorMessage;
323  QVariantList result;
324  try
325  {
326  const auto jObj( json::parse( json.toStdString() ) );
327  if ( ! jObj.is_array() )
328  {
329  throw json::parse_error::create( 0, 0, QStringLiteral( "JSON value must be an array" ).toStdString() );
330  }
331  for ( const auto &item : jObj )
332  {
333  // Create a QVariant from the array item
334  QVariant v;
335  if ( item.is_number_integer() )
336  {
337  v = item.get<int>();
338  }
339  else if ( item.is_number_unsigned() )
340  {
341  v = item.get<unsigned>();
342  }
343  else if ( item.is_number_float() )
344  {
345  // Note: it's a double and not a float on purpose
346  v = item.get<double>();
347  }
348  else if ( item.is_string() )
349  {
350  v = QString::fromStdString( item.get<std::string>() );
351  }
352  else if ( item.is_boolean() )
353  {
354  v = item.get<bool>();
355  }
356  else if ( item.is_null() )
357  {
358  // Fallback to int
359  v = QVariant( type == QVariant::Type::Invalid ? QVariant::Type::Int : type );
360  }
361 
362  // If a destination type was specified (it's not invalid), try to convert
363  if ( type != QVariant::Invalid )
364  {
365  if ( ! v.convert( static_cast<int>( type ) ) )
366  {
367  QgsLogger::warning( QStringLiteral( "Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
368  }
369  else
370  {
371  result.push_back( v );
372  }
373  }
374  else
375  {
376  result.push_back( v );
377  }
378  }
379  }
380  catch ( json::parse_error &ex )
381  {
382  errorMessage = ex.what();
383  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
384  }
385 
386  return result;
387 }
388 
389 json QgsJsonUtils::jsonFromVariant( const QVariant &val )
390 {
391  if ( val.isNull() || ! val.isValid() )
392  {
393  return nullptr;
394  }
395  json j;
396  if ( val.type() == QVariant::Type::Map )
397  {
398  const QVariantMap &vMap = val.toMap();
399  json jMap = json::object();
400  for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
401  {
402  jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() );
403  }
404  j = jMap;
405  }
406  else if ( val.type() == QVariant::Type::List || val.type() == QVariant::Type::StringList )
407  {
408  const QVariantList &vList = val.toList();
409  json jList = json::array();
410  for ( const auto &v : vList )
411  {
412  jList.push_back( jsonFromVariant( v ) );
413  }
414  j = jList;
415  }
416  else
417  {
418  switch ( val.userType() )
419  {
420  case QMetaType::Int:
421  case QMetaType::UInt:
422  case QMetaType::LongLong:
423  case QMetaType::ULongLong:
424  j = val.toLongLong();
425  break;
426  case QMetaType::Double:
427  case QMetaType::Float:
428  j = val.toDouble();
429  break;
430  case QMetaType::Bool:
431  j = val.toBool();
432  break;
433  default:
434  j = val.toString().toStdString();
435  break;
436  }
437  }
438  return j;
439 }
440 
441 QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
442 {
443  std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
444  QVariant result;
445  QString errorMessage;
446  if ( jObj.is_array() )
447  {
448  QVariantList results;
449  for ( const auto &item : jObj )
450  {
451  results.push_back( _parser( item ) );
452  }
453  result = results;
454  }
455  else if ( jObj.is_object() )
456  {
457  QVariantMap results;
458  for ( const auto &item : jObj.items() )
459  {
460  const auto key { QString::fromStdString( item.key() ) };
461  const auto value { _parser( item.value() ) };
462  results[ key ] = value;
463  }
464  result = results;
465  }
466  else
467  {
468  if ( jObj.is_number_integer() )
469  {
470  result = jObj.get<int>();
471  }
472  else if ( jObj.is_number_unsigned() )
473  {
474  result = jObj.get<unsigned>();
475  }
476  else if ( jObj.is_boolean() )
477  {
478  result = jObj.get<bool>();
479  }
480  else if ( jObj.is_number_float() )
481  {
482  // Note: it's a double and not a float on purpose
483  result = jObj.get<double>();
484  }
485  else if ( jObj.is_string() )
486  {
487  result = QString::fromStdString( jObj.get<std::string>() );
488  }
489  else if ( jObj.is_null() )
490  {
491  // Do nothing (leave invalid)
492  }
493  }
494  return result;
495  }
496  };
497 
498  try
499  {
500  const json j = json::parse( jsonString );
501  return _parser( j );
502  }
503  catch ( json::parse_error &ex )
504  {
505  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( QString::fromStdString( ex.what() ),
506  QString::fromStdString( jsonString ) ) );
507  }
508  return QVariant();
509 }
510 
511 QVariant QgsJsonUtils::parseJson( const QString &jsonString )
512 {
513  return parseJson( jsonString.toStdString() );
514 }
515 
516 json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
517 {
518  QgsFields fields = feature.fields();
519  json attrs;
520  for ( int i = 0; i < fields.count(); ++i )
521  {
522  QVariant val = feature.attributes().at( i );
523 
524  if ( layer )
525  {
526  QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
529  val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
530  }
531  attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
532  }
533  return attrs;
534 }
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QVariantMap config() const
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
json exportFeatureToJsonObject(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant()) const
Returns a QJsonObject representation of a feature.
QString name
Definition: qgsfield.h:58
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS for feature geometries.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
void setVectorLayer(QgsVectorLayer *vectorLayer)
Sets the associated vector layer (required for related attribute export).
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source CRS for feature geometries.
QString exportFeature(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant(), int indent=-1) const
Returns a GeoJSON string representation of a feature.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:438
virtual QString id() const =0
Returns a unique id for this field formatter.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
QgsFields fields
Definition: qgsfeature.h:66
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsVectorLayer * vectorLayer() const
Returns the associated vector layer, if set.
QString exportFeatures(const QgsFeatureList &features, int indent=-1) const
Returns a GeoJSON string representation of a list of features (feature collection).
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
static QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a GeoJSON string representing a collection of features...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< QgsRelation > referencedRelations(QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR...
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsRelationManager relationManager
Definition: qgsproject.h:100
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a GeoJSON string to a collection of features.
static json exportAttributesToJsonObject(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a json object.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
static QString exportAttributes(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a JSON map type.
A field formatter helps to handle and display values for a field.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsJsonExporter(QgsVectorLayer *vectorLayer=nullptr, int precision=6)
Constructor for QgsJsonExporter.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
json exportFeaturesToJsonObject(const QgsFeatureList &features) const
Returns a JSON object representation of a list of features (feature collection).
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
virtual json asJsonObject(int precision=17) const
Exports the geometry to a json object.
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places...
Definition: qgis.h:328
Holder for the widget type and its configuration for a field.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:442
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
static QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsFieldFormatter * fallbackFieldFormatter() const
Returns a basic fallback field formatter which can be used to represent any field in an unspectacular...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned...
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg)
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.