QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  box.xMinimum(),
124  box.yMinimum(),
125  box.xMaximum(),
126  box.yMaximum()
127  }
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  json data
229  {
230  { "type", "FeatureCollection" },
231  { "features", json::array() }
232  };
233  const auto constFeatures = features;
234  for ( const QgsFeature &feature : constFeatures )
235  {
236  data["features"].push_back( exportFeatureToJsonObject( feature ) );
237  }
238  return QString::fromStdString( data.dump( indent ) );
239 }
240 
241 //
242 // QgsJsonUtils
243 //
244 
245 QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
246 {
247  return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
248 }
249 
250 QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
251 {
252  return QgsOgrUtils::stringToFields( string, encoding );
253 }
254 
255 QString QgsJsonUtils::encodeValue( const QVariant &value )
256 {
257  if ( value.isNull() )
258  return QStringLiteral( "null" );
259 
260  switch ( value.type() )
261  {
262  case QVariant::Int:
263  case QVariant::UInt:
264  case QVariant::LongLong:
265  case QVariant::ULongLong:
266  case QVariant::Double:
267  return value.toString();
268 
269  case QVariant::Bool:
270  return value.toBool() ? "true" : "false";
271 
272  case QVariant::StringList:
273  case QVariant::List:
274  case QVariant::Map:
275  return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
276 
277  default:
278  case QVariant::String:
279  QString v = value.toString()
280  .replace( '\\', QLatin1String( "\\\\" ) )
281  .replace( '"', QLatin1String( "\\\"" ) )
282  .replace( '\r', QLatin1String( "\\r" ) )
283  .replace( '\b', QLatin1String( "\\b" ) )
284  .replace( '\t', QLatin1String( "\\t" ) )
285  .replace( '/', QLatin1String( "\\/" ) )
286  .replace( '\n', QLatin1String( "\\n" ) );
287 
288  return v.prepend( '"' ).append( '"' );
289  }
290 }
291 
292 QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
293 {
294  QgsFields fields = feature.fields();
295  QString attrs;
296  for ( int i = 0; i < fields.count(); ++i )
297  {
298  if ( i > 0 )
299  attrs += QLatin1String( ",\n" );
300 
301  QVariant val = feature.attributes().at( i );
302 
303  if ( layer )
304  {
305  QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
308  val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
309  }
310 
311  attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
312  }
313  return attrs.prepend( '{' ).append( '}' );
314 }
315 
316 QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
317 {
318  QString errorMessage;
319  QVariantList result;
320  try
321  {
322  const auto jObj( json::parse( json.toStdString() ) );
323  if ( ! jObj.is_array() )
324  {
325  throw json::parse_error::create( 0, 0, QStringLiteral( "JSON value must be an array" ).toStdString() );
326  }
327  for ( const auto &item : jObj )
328  {
329  // Create a QVariant from the array item
330  QVariant v;
331  if ( item.is_number_integer() )
332  {
333  v = item.get<int>();
334  }
335  else if ( item.is_number_unsigned() )
336  {
337  v = item.get<unsigned>();
338  }
339  else if ( item.is_number_float() )
340  {
341  // Note: it's a double and not a float on purpose
342  v = item.get<double>();
343  }
344  else if ( item.is_string() )
345  {
346  v = QString::fromStdString( item.get<std::string>() );
347  }
348  else if ( item.is_boolean() )
349  {
350  v = item.get<bool>();
351  }
352  else if ( item.is_null() )
353  {
354  // Fallback to int
355  v = QVariant( type == QVariant::Type::Invalid ? QVariant::Type::Int : type );
356  }
357 
358  // If a destination type was specified (it's not invalid), try to convert
359  if ( type != QVariant::Invalid )
360  {
361  if ( ! v.convert( static_cast<int>( type ) ) )
362  {
363  QgsLogger::warning( QStringLiteral( "Cannot convert json array element to specified type, ignoring: %1" ).arg( v.toString() ) );
364  }
365  else
366  {
367  result.push_back( v );
368  }
369  }
370  else
371  {
372  result.push_back( v );
373  }
374  }
375  }
376  catch ( json::parse_error &ex )
377  {
378  errorMessage = ex.what();
379  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( ex.what(), json ) );
380  }
381 
382  return result;
383 }
384 
385 json QgsJsonUtils::jsonFromVariant( const QVariant &val )
386 {
387  if ( val.isNull() || ! val.isValid() )
388  {
389  return nullptr;
390  }
391  json j;
392  if ( val.type() == QVariant::Type::Map )
393  {
394  const auto vMap { val.toMap() };
395  auto jMap { json::object() };
396  for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ )
397  {
398  jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() );
399  }
400  j = jMap;
401  }
402  else if ( val.type() == QVariant::Type::List )
403  {
404  const auto vList{ val.toList() };
405  auto jList { json::array() };
406  for ( const auto &v : vList )
407  {
408  jList.push_back( jsonFromVariant( v ) );
409  }
410  j = jList;
411  }
412  else
413  {
414  switch ( val.userType() )
415  {
416  case QMetaType::Int:
417  case QMetaType::UInt:
418  case QMetaType::LongLong:
419  case QMetaType::ULongLong:
420  j = val.toLongLong();
421  break;
422  case QMetaType::Double:
423  case QMetaType::Float:
424  j = val.toDouble();
425  break;
426  case QMetaType::Bool:
427  j = val.toBool();
428  break;
429  default:
430  j = val.toString().toStdString();
431  break;
432  }
433  }
434  return j;
435 }
436 
437 QVariant QgsJsonUtils::parseJson( const std::string &jsonString )
438 {
439  std::function<QVariant( json )> _parser { [ & ]( json jObj ) -> QVariant {
440  QVariant result;
441  QString errorMessage;
442  if ( jObj.is_array() )
443  {
444  QVariantList results;
445  for ( const auto &item : jObj )
446  {
447  results.push_back( _parser( item ) );
448  }
449  result = results;
450  }
451  else if ( jObj.is_object() )
452  {
453  QVariantMap results;
454  for ( const auto &item : jObj.items() )
455  {
456  const auto key { QString::fromStdString( item.key() ) };
457  const auto value { _parser( item.value() ) };
458  results[ key ] = value;
459  }
460  result = results;
461  }
462  else
463  {
464  if ( jObj.is_number_integer() )
465  {
466  result = jObj.get<int>();
467  }
468  else if ( jObj.is_number_unsigned() )
469  {
470  result = jObj.get<unsigned>();
471  }
472  else if ( jObj.is_boolean() )
473  {
474  result = jObj.get<bool>();
475  }
476  else if ( jObj.is_number_float() )
477  {
478  // Note: it's a double and not a float on purpose
479  result = jObj.get<double>();
480  }
481  else if ( jObj.is_string() )
482  {
483  result = QString::fromStdString( jObj.get<std::string>() );
484  }
485  else if ( jObj.is_null() )
486  {
487  // Do nothing (leave invalid)
488  }
489  }
490  return result;
491  }
492  };
493 
494  try
495  {
496  const json j = json::parse( jsonString );
497  return _parser( j );
498  }
499  catch ( json::parse_error &ex )
500  {
501  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( QString::fromStdString( ex.what() ),
502  QString::fromStdString( jsonString ) ) );
503  }
504  return QVariant();
505 }
506 
507 QVariant QgsJsonUtils::parseJson( const QString &jsonString )
508 {
509  return parseJson( jsonString.toStdString() );
510 }
511 
512 json QgsJsonUtils::exportAttributesToJsonObject( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
513 {
514  QgsFields fields = feature.fields();
515  json attrs;
516  for ( int i = 0; i < fields.count(); ++i )
517  {
518  QVariant val = feature.attributes().at( i );
519 
520  if ( layer )
521  {
522  QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
525  val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
526  }
527  attrs[fields.at( i ).name().toStdString()] = jsonFromVariant( val );
528  }
529  return attrs;
530 }
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:111
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:410
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.
nlohmann::json json
Definition: qgsjsonutils.h:27
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
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.
Holder for the widget type and its configuration for a field.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
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:430
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:85
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.