Skip to content
Permalink
Browse files

Fixes #20053 decimal separator in csv files

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 859b39a63438e426f227114747a49ddcfd49bc09
Showing with 59 additions and 2 deletions.
  1. +8 −1 src/core/qgsjsonutils.cpp
  2. +51 −1 tests/src/python/test_qgsjsonutils.py
@@ -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
@@ -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 )
{
@@ -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 );
}

@@ -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")
@@ -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.
You can’t perform that action at this time.