Skip to content

Commit 86ce441

Browse files
authoredMay 26, 2017
Merge pull request #4627 from nyalldawson/json_format
Use field formatter when exporting feature attributes to JSON
2 parents 9cfe70c + 5b0bc93 commit 86ce441

File tree

13 files changed

+207
-99
lines changed

13 files changed

+207
-99
lines changed
 

‎.ci/travis/linux/blacklist.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# black list
2-
PyQgsJSONUtils
2+
PyQgsJsonUtils
33
PyQgsLocalServer
44
PyQgsPalLabelingServer
55
qgis_composermapgridtest

‎doc/api_break.dox

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ Renamed Classes {#qgis_api_break_3_0_renamed_classes}
104104
<tr><td>QgsGraduatedSymbolRendererV2Model<td>QgsGraduatedSymbolRendererModel
105105
<tr><td>QgsGraduatedSymbolRendererV2ViewStyle<td>QgsGraduatedSymbolRendererViewStyle
106106
<tr><td>QgsGraduatedSymbolRendererV2Widget<td>QgsGraduatedSymbolRendererWidget
107+
<tr><td>QgsJSONExporter<td>QgsJsonExporter
108+
<tr><td>QgsJSONUtils<td>QgsJsonUtils
107109
<tr><td>QgsLabelingEngineV2<td>QgsLabelingEngine
108110
<tr><td>QgsLegendModelV2<td>QgsLegendModel
109111
<tr><td>QgsLegendSymbolItemV2<td>QgsLegendSymbolItem

‎python/core/qgsjsonutils.sip

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111

1212

13-
class QgsJSONExporter
13+
class QgsJsonExporter
1414
{
1515
%Docstring
1616
Handles exporting QgsFeature features to GeoJSON features.
@@ -25,9 +25,9 @@ class QgsJSONExporter
2525
%End
2626
public:
2727

28-
QgsJSONExporter( const QgsVectorLayer *vectorLayer = 0, int precision = 6 );
28+
QgsJsonExporter( QgsVectorLayer *vectorLayer = 0, int precision = 6 );
2929
%Docstring
30-
Constructor for QgsJSONExporter.
30+
Constructor for QgsJsonExporter.
3131
\param vectorLayer associated vector layer (required for related attribute export)
3232
\param precision maximum number of decimal places to use for geometry coordinates,
3333
the RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
@@ -94,7 +94,7 @@ class QgsJSONExporter
9494
:rtype: bool
9595
%End
9696

97-
void setVectorLayer( const QgsVectorLayer *vectorLayer );
97+
void setVectorLayer( QgsVectorLayer *vectorLayer );
9898
%Docstring
9999
Sets the associated vector layer (required for related attribute export). This will automatically
100100
update the sourceCrs() to match.
@@ -199,7 +199,7 @@ class QgsJSONExporter
199199
};
200200

201201

202-
class QgsJSONUtils
202+
class QgsJsonUtils
203203
{
204204
%Docstring
205205
Helper utilities for working with JSON and GeoJSON conversions.
@@ -247,10 +247,15 @@ class QgsJSONUtils
247247
:rtype: str
248248
%End
249249

250-
static QString exportAttributes( const QgsFeature &feature );
250+
static QString exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer = 0,
251+
QVector<QVariant> attributeWidgetCaches = QVector<QVariant>() );
251252
%Docstring
252253
Exports all attributes from a QgsFeature as a JSON map type.
253254
\param feature feature to export
255+
\param layer optional associated vector layer. If specified, this allows
256+
richer export utilising settings like the layer's fields widget configuration.
257+
\param attributeWidgetCaches optional widget configuration cache. Can be used
258+
to speed up exporting the attributes for multiple features from the same layer.
254259
:rtype: str
255260
%End
256261

‎src/app/qgsclipboard.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ QString QgsClipboard::generateClipboardText() const
142142
}
143143
case GeoJSON:
144144
{
145-
QgsJSONExporter exporter;
145+
QgsJsonExporter exporter;
146146
exporter.setSourceCrs( mCRS );
147147
return exporter.exportFeatures( mFeatureClipboard );
148148
}

‎src/core/composer/qgscomposerhtml.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ void QgsComposerHtml::setExpressionContext( const QgsFeature &feature, QgsVector
551551
}
552552

553553
// create JSON representation of feature
554-
QgsJSONExporter exporter( layer );
554+
QgsJsonExporter exporter( layer );
555555
exporter.setIncludeRelated( true );
556556
mAtlasFeatureJSON = exporter.exportFeature( feature );
557557
}

‎src/core/qgsjsonutils.cpp

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@
2323
#include "qgsproject.h"
2424
#include "qgscsexception.h"
2525
#include "qgslogger.h"
26+
#include "qgsfieldformatterregistry.h"
27+
#include "qgsfieldformatter.h"
2628

2729
#include <QJsonDocument>
2830
#include <QJsonArray>
2931

30-
QgsJSONExporter::QgsJSONExporter( const QgsVectorLayer *vectorLayer, int precision )
32+
QgsJsonExporter::QgsJsonExporter( QgsVectorLayer *vectorLayer, int precision )
3133
: mPrecision( precision )
3234
, mIncludeGeometry( true )
3335
, mIncludeAttributes( true )
3436
, mIncludeRelatedAttributes( false )
35-
, mLayerId( vectorLayer ? vectorLayer->id() : QString() )
37+
, mLayer( vectorLayer )
3638
{
3739
if ( vectorLayer )
3840
{
@@ -42,39 +44,39 @@ QgsJSONExporter::QgsJSONExporter( const QgsVectorLayer *vectorLayer, int precisi
4244
mTransform.setDestinationCrs( QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) );
4345
}
4446

45-
void QgsJSONExporter::setVectorLayer( const QgsVectorLayer *vectorLayer )
47+
void QgsJsonExporter::setVectorLayer( QgsVectorLayer *vectorLayer )
4648
{
47-
mLayerId = vectorLayer ? vectorLayer->id() : QString();
49+
mLayer = vectorLayer;
4850
if ( vectorLayer )
4951
{
5052
mCrs = vectorLayer->crs();
5153
mTransform.setSourceCrs( mCrs );
5254
}
5355
}
5456

55-
QgsVectorLayer *QgsJSONExporter::vectorLayer() const
57+
QgsVectorLayer *QgsJsonExporter::vectorLayer() const
5658
{
57-
return qobject_cast< QgsVectorLayer * >( QgsProject::instance()->mapLayer( mLayerId ) );
59+
return mLayer.data();
5860
}
5961

60-
void QgsJSONExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
62+
void QgsJsonExporter::setSourceCrs( const QgsCoordinateReferenceSystem &crs )
6163
{
6264
mCrs = crs;
6365
mTransform.setSourceCrs( mCrs );
6466
}
6567

66-
QgsCoordinateReferenceSystem QgsJSONExporter::sourceCrs() const
68+
QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const
6769
{
6870
return mCrs;
6971
}
7072

71-
QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
73+
QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
7274
const QVariant &id ) const
7375
{
7476
QString s = QStringLiteral( "{\n \"type\":\"Feature\",\n" );
7577

7678
// ID
77-
s += QStringLiteral( " \"id\":%1,\n" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJSONUtils::encodeValue( id ) );
79+
s += QStringLiteral( " \"id\":%1,\n" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJsonUtils::encodeValue( id ) );
7880

7981
QgsGeometry geom = feature.geometry();
8082
if ( !geom.isNull() && mIncludeGeometry )
@@ -119,7 +121,7 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
119121

120122
if ( mIncludeAttributes )
121123
{
122-
QgsFields fields = feature.fields();
124+
QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
123125

124126
for ( int i = 0; i < fields.count(); ++i )
125127
{
@@ -130,7 +132,15 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
130132
properties += QLatin1String( ",\n" );
131133
QVariant val = feature.attributes().at( i );
132134

133-
properties += QStringLiteral( " \"%1\":%2" ).arg( fields.at( i ).name(), QgsJSONUtils::encodeValue( val ) );
135+
if ( mLayer )
136+
{
137+
QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
138+
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
139+
if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
140+
val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
141+
}
142+
143+
properties += QStringLiteral( " \"%1\":%2" ).arg( fields.at( i ).name(), QgsJsonUtils::encodeValue( val ) );
134144

135145
++attributeCounter;
136146
}
@@ -144,17 +154,16 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
144154
if ( attributeCounter > 0 )
145155
properties += QLatin1String( ",\n" );
146156

147-
properties += QStringLiteral( " \"%1\":%2" ).arg( it.key(), QgsJSONUtils::encodeValue( it.value() ) );
157+
properties += QStringLiteral( " \"%1\":%2" ).arg( it.key(), QgsJsonUtils::encodeValue( it.value() ) );
148158

149159
++attributeCounter;
150160
}
151161
}
152162

153163
// related attributes
154-
QgsVectorLayer *vl = vectorLayer();
155-
if ( vl && mIncludeRelatedAttributes )
164+
if ( mLayer && mIncludeRelatedAttributes )
156165
{
157-
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( vl );
166+
QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
158167
Q_FOREACH ( const QgsRelation &relation, relations )
159168
{
160169
if ( attributeCounter > 0 )
@@ -167,14 +176,24 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
167176
if ( childLayer )
168177
{
169178
QgsFeatureIterator it = childLayer->getFeatures( req );
179+
QVector<QVariant> attributeWidgetCaches;
180+
int fieldIndex = 0;
181+
Q_FOREACH ( const QgsField &field, childLayer->fields() )
182+
{
183+
QgsEditorWidgetSetup setup = field.editorWidgetSetup();
184+
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
185+
attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
186+
fieldIndex++;
187+
}
188+
170189
QgsFeature relatedFet;
171190
int relationFeatures = 0;
172191
while ( it.nextFeature( relatedFet ) )
173192
{
174193
if ( relationFeatures > 0 )
175194
relatedFeatureAttributes += QLatin1String( ",\n" );
176195

177-
relatedFeatureAttributes += QgsJSONUtils::exportAttributes( relatedFet );
196+
relatedFeatureAttributes += QgsJsonUtils::exportAttributes( relatedFet, childLayer, attributeWidgetCaches );
178197
relationFeatures++;
179198
}
180199
}
@@ -204,7 +223,7 @@ QString QgsJSONExporter::exportFeature( const QgsFeature &feature, const QVarian
204223
return s;
205224
}
206225

207-
QString QgsJSONExporter::exportFeatures( const QgsFeatureList &features ) const
226+
QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features ) const
208227
{
209228
QStringList featureJSON;
210229
Q_FOREACH ( const QgsFeature &feature, features )
@@ -218,20 +237,20 @@ QString QgsJSONExporter::exportFeatures( const QgsFeatureList &features ) const
218237

219238

220239
//
221-
// QgsJSONUtils
240+
// QgsJsonUtils
222241
//
223242

224-
QgsFeatureList QgsJSONUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
243+
QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
225244
{
226245
return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
227246
}
228247

229-
QgsFields QgsJSONUtils::stringToFields( const QString &string, QTextCodec *encoding )
248+
QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
230249
{
231250
return QgsOgrUtils::stringToFields( string, encoding );
232251
}
233252

234-
QString QgsJSONUtils::encodeValue( const QVariant &value )
253+
QString QgsJsonUtils::encodeValue( const QVariant &value )
235254
{
236255
if ( value.isNull() )
237256
return QStringLiteral( "null" );
@@ -268,7 +287,7 @@ QString QgsJSONUtils::encodeValue( const QVariant &value )
268287
}
269288
}
270289

271-
QString QgsJSONUtils::exportAttributes( const QgsFeature &feature )
290+
QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, QVector<QVariant> attributeWidgetCaches )
272291
{
273292
QgsFields fields = feature.fields();
274293
QString attrs;
@@ -278,12 +297,21 @@ QString QgsJSONUtils::exportAttributes( const QgsFeature &feature )
278297
attrs += QLatin1String( ",\n" );
279298

280299
QVariant val = feature.attributes().at( i );
300+
301+
if ( layer )
302+
{
303+
QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
304+
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
305+
if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
306+
val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
307+
}
308+
281309
attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
282310
}
283311
return attrs.prepend( '{' ).append( '}' );
284312
}
285313

286-
QVariantList QgsJSONUtils::parseArray( const QString &json, QVariant::Type type )
314+
QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
287315
{
288316
QJsonParseError error;
289317
const QJsonDocument jsonDoc = QJsonDocument::fromJson( json.toUtf8(), &error );

‎src/core/qgsjsonutils.h

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,29 @@
2121
#include "qgscoordinatereferencesystem.h"
2222
#include "qgscoordinatetransform.h"
2323
#include "qgsfields.h"
24+
#include "qgsvectorlayer.h"
2425

2526
class QTextCodec;
26-
class QgsVectorLayer;
2727

2828
/** \ingroup core
29-
* \class QgsJSONExporter
29+
* \class QgsJsonExporter
3030
* \brief Handles exporting QgsFeature features to GeoJSON features.
3131
*
3232
* Note that geometries will be automatically reprojected to WGS84 to match GeoJSON spec
3333
* if either the source vector layer or source CRS is set.
3434
* \since QGIS 2.16
3535
*/
3636

37-
class CORE_EXPORT QgsJSONExporter
37+
class CORE_EXPORT QgsJsonExporter
3838
{
3939
public:
4040

41-
/** Constructor for QgsJSONExporter.
41+
/** Constructor for QgsJsonExporter.
4242
* \param vectorLayer associated vector layer (required for related attribute export)
4343
* \param precision maximum number of decimal places to use for geometry coordinates,
4444
* the RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
4545
*/
46-
QgsJSONExporter( const QgsVectorLayer *vectorLayer = nullptr, int precision = 6 );
46+
QgsJsonExporter( QgsVectorLayer *vectorLayer = nullptr, int precision = 6 );
4747

4848
/** Sets the maximum number of decimal places to use in geometry coordinates.
4949
* The RFC 7946 GeoJSON specification recommends limiting coordinate precision to 6
@@ -97,7 +97,7 @@ class CORE_EXPORT QgsJSONExporter
9797
* \param vectorLayer vector layer
9898
* \see vectorLayer()
9999
*/
100-
void setVectorLayer( const QgsVectorLayer *vectorLayer );
100+
void setVectorLayer( QgsVectorLayer *vectorLayer );
101101

102102
/** Returns the associated vector layer, if set.
103103
* \see setVectorLayer()
@@ -193,8 +193,8 @@ class CORE_EXPORT QgsJSONExporter
193193
//! Whether to include attributes from related features in JSON export
194194
bool mIncludeRelatedAttributes;
195195

196-
//! Layer ID of associated vector layer. Required for related attribute export.
197-
QString mLayerId;
196+
//! Associated vector layer. Required for related attribute export.
197+
QPointer< QgsVectorLayer > mLayer;
198198

199199
QgsCoordinateReferenceSystem mCrs;
200200

@@ -203,12 +203,12 @@ class CORE_EXPORT QgsJSONExporter
203203
};
204204

205205
/** \ingroup core
206-
* \class QgsJSONUtils
206+
* \class QgsJsonUtils
207207
* \brief Helper utilities for working with JSON and GeoJSON conversions.
208208
* \since QGIS 2.16
209209
*/
210210

211-
class CORE_EXPORT QgsJSONUtils
211+
class CORE_EXPORT QgsJsonUtils
212212
{
213213
public:
214214

@@ -240,8 +240,13 @@ class CORE_EXPORT QgsJSONUtils
240240

241241
/** Exports all attributes from a QgsFeature as a JSON map type.
242242
* \param feature feature to export
243+
* \param layer optional associated vector layer. If specified, this allows
244+
* richer export utilising settings like the layer's fields widget configuration.
245+
* \param attributeWidgetCaches optional widget configuration cache. Can be used
246+
* to speed up exporting the attributes for multiple features from the same layer.
243247
*/
244-
static QString exportAttributes( const QgsFeature &feature );
248+
static QString exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer = nullptr,
249+
QVector<QVariant> attributeWidgetCaches = QVector<QVariant>() );
245250

246251
/** Parse a simple array (depth=1).
247252
* \param json the JSON to parse

‎src/providers/spatialite/qgsspatialitefeatureiterator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt *stmt,
554554
if ( type == QVariant::List || type == QVariant::StringList )
555555
{
556556
// assume arrays are stored as JSON
557-
QVariant result = QVariant( QgsJSONUtils::parseArray( txt, subType ) );
557+
QVariant result = QVariant( QgsJsonUtils::parseArray( txt, subType ) );
558558
result.convert( type );
559559
return result;
560560
}

0 commit comments

Comments
 (0)
Please sign in to comment.