Skip to content

Commit

Permalink
Fixes #20053 decimal separator in csv files
Browse files Browse the repository at this point in the history
Actually it had nothing to do with CSV being the
source, but it was the json exporter passing
the values through all field formatters except for
the fallback.

This resulted in all fields using a 'Range' formatter
(which is the default for all numeric types) passing
through the formatter and being returned as strings
in the json. Worse, if the locale was not a "dot"
locale and decimal separator was on, the resulting
string could not be easily converted into its original
numeric type.

Now, instead of checking for the fallback formatter
only, there is a white list of formatters that
can be applied when we want a json.

This is a temporary solution because the "right" way
to do it would be either a flag in the formatter to
tell if it can be applied when converting to json
and/or other "data" formats (csv etc.) or a different
new method similar to representValue.
  • Loading branch information
elpaso committed Oct 9, 2018
1 parent 1a5a23d commit 859b39a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
9 changes: 8 additions & 1 deletion src/core/qgsjsonutils.cpp
Expand Up @@ -70,6 +70,7 @@ QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const
QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
const QVariant &id ) const
{

QString s = QStringLiteral( "{\n \"type\":\"Feature\",\n" );

// ID
Expand Down Expand Up @@ -119,6 +120,12 @@ QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVarian
if ( mIncludeAttributes )
{
QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
// List of formatters through we want to pass the values
QStringList formattersWhiteList;
formattersWhiteList << QStringLiteral( "KeyValue" )
<< QStringLiteral( "List" )
<< QStringLiteral( "ValueRelation" )
<< QStringLiteral( "ValueMap" );

for ( int i = 0; i < fields.count(); ++i )
{
Expand All @@ -133,7 +140,7 @@ QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVarian
{
QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter != QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter() )
if ( formattersWhiteList.contains( fieldFormatter->id() ) )
val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
}

Expand Down
52 changes: 51 additions & 1 deletion tests/src/python/test_qgsjsonutils.py
Expand Up @@ -31,7 +31,7 @@
QgsRelation,
QgsEditorWidgetSetup
)
from qgis.PyQt.QtCore import QVariant, QTextCodec
from qgis.PyQt.QtCore import QVariant, QTextCodec, QLocale

start_app()
codec = QTextCodec.codecForName("System")
Expand Down Expand Up @@ -676,6 +676,56 @@ def testExportFeatures(self):
]}"""
self.assertEqual(exporter.exportFeatures([feature, feature2]), expected)

def testExportFeaturesWithLocale_regression20053(self):
""" Test exporting feature export with range widgets and locale different than C
Regression: https://issues.qgis.org/issues/20053 - decimal separator in csv files
"""

source = QgsVectorLayer("Point?field=name:string&field=cost:double&field=population:int&field=date:date",
"parent", "memory")
self.assertTrue(source.isValid())
fields = source.fields()

feature = QgsFeature(fields, 5)
feature.setGeometry(QgsGeometry(QgsPoint(5, 6)))
feature.setAttributes(['Valsier Peninsula', 6.8, 198000, '2018-09-10'])

exporter = QgsJsonExporter()

# single feature
expected = """{ "type": "FeatureCollection",
"features":[
{
"type":"Feature",
"id":5,
"geometry":
{"type": "Point", "coordinates": [5, 6]},
"properties":{
"name":"Valsier Peninsula",
"cost":6.8,
"population":198000,
"date":"2018-09-10"
}
}
]}"""
self.assertEqual(exporter.exportFeatures([feature]), expected)

setup = QgsEditorWidgetSetup('Range', {
'AllowNull': True,
'Max': 2147483647,
'Min': -2147483648,
'Precision': 4,
'Step': 1,
'Style': 'SpinBox'
}
)
source.setEditorWidgetSetup(1, setup)
source.setEditorWidgetSetup(2, setup)

QLocale.setDefault(QLocale('it'))
exporter.setVectorLayer(source)
self.assertEqual(exporter.exportFeatures([feature]), expected)


if __name__ == "__main__":
unittest.main()

0 comments on commit 859b39a

Please sign in to comment.