Skip to content

Commit 6260f9d

Browse files
Patrick Valsecchipvalsecc
authored andcommitted
Add support for arrays in the spatialite provider
New versions of GDAL/OGR (since trunk@35289) convert list types (StringList, IntegerList, Integer64List and RealList) to a JSON string when it stores a Spatialite table. It sets the column type as JSONSTRINGLIST, JSONINTEGERLIST, JSONINTEGER64LIST or JSONREALLIST.
1 parent cd1d44b commit 6260f9d

File tree

9 files changed

+330
-58
lines changed

9 files changed

+330
-58
lines changed

python/core/qgsjsonutils.sip

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,11 @@ class QgsJSONUtils
197197
* @param feature feature to export
198198
*/
199199
static QString exportAttributes( const QgsFeature& feature );
200+
201+
/** Parse a simple array (depth=1).
202+
* @param json the JSON to parse
203+
* @param type the type of the elements
204+
* @note added in QGIS 3.0
205+
*/
206+
static QVariantList parseArray( const QString& json, QVariant::Type type );
200207
};

src/core/qgsjsonutils.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,82 @@ QString QgsJSONUtils::exportAttributes( const QgsFeature& feature )
309309
return attrs.prepend( '{' ).append( '}' );
310310
}
311311

312+
313+
namespace //TODO: remove when we switch off Qt4
314+
{
315+
void jumpSpace( const QString& txt, int& i )
316+
{
317+
while ( i < txt.length() && txt.at( i ).isSpace() ) ++i;
318+
}
319+
320+
static QString getNextString( const QString& txt, int& i )
321+
{
322+
jumpSpace( txt, i );
323+
QString cur = txt.mid( i );
324+
if ( cur.startsWith( '"' ) )
325+
{ //quoted element
326+
QRegExp stringRe( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
327+
if ( !stringRe.exactMatch( cur ) )
328+
{
329+
return QString::null;
330+
}
331+
i += stringRe.cap( 1 ).length() + 2;
332+
jumpSpace( txt, i );
333+
if ( !txt.mid( i ).startsWith( ',' ) && !txt.mid( i ).startsWith( ']' ) && i < txt.length() )
334+
{
335+
return QString::null;
336+
}
337+
i += 1; // jump the separator
338+
339+
return stringRe.cap( 1 )
340+
.replace( "\\\"", "\"" )
341+
.replace( "\\r", "\r" )
342+
.replace( "\\b", "\b" )
343+
.replace( "\\t", "\t" )
344+
.replace( "\\/", "/" )
345+
.replace( "\\n", "\n" )
346+
.replace( "\\\\", "\\" );
347+
}
348+
else
349+
{ //unquoted element
350+
QString ret;
351+
int sepPos = cur.indexOf( ',' );
352+
if ( sepPos < 0 ) sepPos = cur.indexOf( ']' );
353+
if ( sepPos < 0 )
354+
{
355+
i += cur.length();
356+
return cur.trimmed();
357+
}
358+
i += sepPos + 1;
359+
return cur.left( sepPos ).trimmed();
360+
}
361+
}
362+
}
363+
364+
QVariantList QgsJSONUtils::parseArray( const QString& json, QVariant::Type type )
365+
{
366+
// TODO: switch to the Qt parser when we switch off Qt4
367+
QVariantList result;
368+
int i = 0;
369+
jumpSpace( json, i );
370+
if ( json.at( i++ ) != '[' )
371+
{
372+
return result;
373+
}
374+
while ( i < json.length() )
375+
{
376+
jumpSpace( json, i );
377+
if ( json.at( i ) == ']' )
378+
{
379+
return result;
380+
}
381+
const QString value = getNextString( json, i );
382+
if ( value.isNull() )
383+
{
384+
break;
385+
}
386+
QVariant variant( value );
387+
if ( variant.convert( type ) ) result.append( variant );
388+
}
389+
return result;
390+
}

src/core/qgsjsonutils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ class CORE_EXPORT QgsJSONUtils
240240
*/
241241
static QString exportAttributes( const QgsFeature& feature );
242242

243+
/** Parse a simple array (depth=1).
244+
* @param json the JSON to parse
245+
* @param type the type of the elements
246+
* @note added in QGIS 3.0
247+
*/
248+
static QVariantList parseArray( const QString& json, QVariant::Type type );
243249
};
244250

245251
#endif // QGSJSONUTILS_H

src/providers/spatialite/qgsspatialitefeatureiterator.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
#include "qgsspatialiteprovider.h"
2020
#include "qgssqliteexpressioncompiler.h"
2121

22-
#include "qgsgeometry.h"
23-
#include "qgslogger.h"
24-
#include "qgsmessagelog.h"
22+
#include <qgsgeometry.h>
23+
#include <qgslogger.h>
24+
#include <qgsmessagelog.h>
25+
#include <qgsjsonutils.h>
26+
2527
#include <QSettings>
2628

2729
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
@@ -499,22 +501,24 @@ bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &f
499501
{
500502
if ( ic <= mRequest.subsetOfAttributes().size() )
501503
{
502-
int attrIndex = mRequest.subsetOfAttributes()[ic-1];
503-
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
504+
const int attrIndex = mRequest.subsetOfAttributes()[ic-1];
505+
const QgsField field = mSource->mFields.at( attrIndex );
506+
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
504507
}
505508
}
506509
else
507510
{
508-
int attrIndex = ic - 1;
509-
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, mSource->mFields.at( attrIndex ).type() ) );
511+
const int attrIndex = ic - 1;
512+
const QgsField field = mSource->mFields.at( attrIndex );
513+
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
510514
}
511515
}
512516
}
513517

514518
return true;
515519
}
516520

517-
QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type )
521+
QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type, QVariant::Type subType )
518522
{
519523
if ( sqlite3_column_type( stmt, ic ) == SQLITE_INTEGER )
520524
{
@@ -539,8 +543,14 @@ QVariant QgsSpatiaLiteFeatureIterator::getFeatureAttribute( sqlite3_stmt* stmt,
539543
if ( sqlite3_column_type( stmt, ic ) == SQLITE_TEXT )
540544
{
541545
// TEXT value
542-
const char *txt = ( const char * ) sqlite3_column_text( stmt, ic );
543-
return QString::fromUtf8( txt );
546+
const QString txt = QString::fromUtf8(( const char * ) sqlite3_column_text( stmt, ic ) );
547+
if ( type == QVariant::List || type == QVariant::StringList )
548+
{ // assume arrays are stored as JSON
549+
QVariant result = QVariant( QgsJSONUtils::parseArray( txt, subType ) );
550+
result.convert( type );
551+
return result;
552+
}
553+
return txt;
544554
}
545555

546556
// assuming NULL

src/providers/spatialite/qgsspatialitefeatureiterator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class QgsSpatiaLiteFeatureIterator : public QgsAbstractFeatureIteratorFromSource
8383
QString quotedPrimaryKey();
8484
bool getFeature( sqlite3_stmt *stmt, QgsFeature &feature );
8585
QString fieldName( const QgsField& fld );
86-
QVariant getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type );
86+
QVariant getFeatureAttribute( sqlite3_stmt* stmt, int ic, QVariant::Type type, QVariant::Type subType );
8787
void getFeatureGeometry( sqlite3_stmt* stmt, int ic, QgsFeature& feature );
8888

8989
//! wrapper of the SQLite database connection

0 commit comments

Comments
 (0)