mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-12 00:02:25 -04:00
improved parsing and building function to handle multidimensional arrays
code mostly taken from this integration in the postgresprovider / postgresconn x
This commit is contained in:
parent
98a271b365
commit
109b4d7f21
@ -14,33 +14,109 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsarrayutils.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include <QDebug>
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
static void jumpSpace( const QString &txt, int &i )
|
||||
{
|
||||
while ( i < txt.length() && txt.at( i ).isSpace() )
|
||||
++i;
|
||||
}
|
||||
|
||||
QString QgsArrayUtils::getNextString( const QString &txt, int &i, const QString &sep )
|
||||
{
|
||||
jumpSpace( txt, i );
|
||||
QString cur = txt.mid( i );
|
||||
if ( cur.startsWith( '"' ) )
|
||||
{
|
||||
QRegExp stringRe( "^\"((?:\\\\.|[^\"\\\\])*)\".*" );
|
||||
if ( !stringRe.exactMatch( cur ) )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Cannot find end of double quoted string: %1" ).arg( txt ), QObject::tr( "PostgresStringUtils" ) );
|
||||
return QString();
|
||||
}
|
||||
i += stringRe.cap( 1 ).length() + 2;
|
||||
jumpSpace( txt, i );
|
||||
if ( !txt.midRef( i ).startsWith( sep ) && i < txt.length() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), QObject::tr( "PostgresStringUtils" ) );
|
||||
return QString();
|
||||
}
|
||||
i += sep.length();
|
||||
return stringRe.cap( 1 ).replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) ).replace( QLatin1String( "\\\\" ), QLatin1String( "\\" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
int sepPos = cur.indexOf( sep );
|
||||
if ( sepPos < 0 )
|
||||
{
|
||||
i += cur.length();
|
||||
return cur.trimmed();
|
||||
}
|
||||
i += sepPos + sep.length();
|
||||
return cur.left( sepPos ).trimmed();
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList QgsArrayUtils::parse( const QString &string )
|
||||
{
|
||||
QVariantList variantList;
|
||||
|
||||
QString newVal = string;
|
||||
if ( newVal.trimmed().startsWith( '{' ) )
|
||||
if ( string.trimmed().startsWith( '{' ) )
|
||||
{
|
||||
newVal = newVal.trimmed().mid( 1 ).mid( 0, newVal.length() - 2 ).prepend( '[' ).append( ']' );
|
||||
//it's a postgres array
|
||||
QString newVal = string.mid( 1, string.length() - 2 );
|
||||
|
||||
if ( !json::accept( newVal.toStdString() ) )
|
||||
if ( newVal.trimmed().startsWith( '{' ) )
|
||||
{
|
||||
//fallback for wrongly stored string data without quotes
|
||||
newVal = string;
|
||||
const QStringList stringList = newVal.remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( ',' );
|
||||
for ( const QString &s : qgis::as_const( stringList ) )
|
||||
//it's a multidimensional array
|
||||
QStringList values;
|
||||
QString subarray = newVal;
|
||||
while ( !subarray.isEmpty() )
|
||||
{
|
||||
variantList.push_back( s );
|
||||
bool escaped = false;
|
||||
int openedBrackets = 1;
|
||||
int i = 0;
|
||||
while ( i < subarray.length() && openedBrackets > 0 )
|
||||
{
|
||||
++i;
|
||||
|
||||
if ( subarray.at( i ) == '}' && !escaped ) openedBrackets--;
|
||||
else if ( subarray.at( i ) == '{' && !escaped ) openedBrackets++;
|
||||
|
||||
escaped = !escaped ? subarray.at( i ) == '\\' : false;
|
||||
}
|
||||
|
||||
variantList.append( subarray.left( ++i ) );
|
||||
i = subarray.indexOf( ',', i );
|
||||
i = i > 0 ? subarray.indexOf( '{', i ) : -1;
|
||||
if ( i == -1 )
|
||||
break;
|
||||
|
||||
subarray = subarray.mid( i );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
while ( i < newVal.length() )
|
||||
{
|
||||
const QString value = getNextString( newVal, i, QStringLiteral( "," ) );
|
||||
if ( value.isNull() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Error parsing PG like array: %1" ).arg( newVal ), QObject::tr( "PostgresStringUtils" ) );
|
||||
break;
|
||||
}
|
||||
variantList.append( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( newVal.trimmed().startsWith( '[' ) )
|
||||
else if ( string.trimmed().startsWith( '[' ) )
|
||||
{
|
||||
//it's a json array
|
||||
QString newVal = string;
|
||||
try
|
||||
{
|
||||
for ( auto &element : json::parse( newVal.toStdString() ) )
|
||||
@ -62,9 +138,11 @@ QVariantList QgsArrayUtils::parse( const QString &string )
|
||||
catch ( json::parse_error &ex )
|
||||
{
|
||||
qDebug() << QString::fromStdString( ex.what() );
|
||||
QgsMessageLog::logMessage( QObject::tr( "Error parsing JSON like array: %1 %2" ).arg( newVal, ex.what() ), QObject::tr( "PostgresStringUtils" ) );
|
||||
}
|
||||
}
|
||||
return variantList;
|
||||
|
||||
}
|
||||
|
||||
QString QgsArrayUtils::build( const QVariantList &list )
|
||||
@ -81,9 +159,16 @@ QString QgsArrayUtils::build( const QVariantList &list )
|
||||
break;
|
||||
default:
|
||||
QString newS = v.toString();
|
||||
newS.replace( '\\', QStringLiteral( R"(\\)" ) );
|
||||
newS.replace( '\"', QStringLiteral( R"(\")" ) );
|
||||
sl.push_back( "\"" + newS + "\"" );
|
||||
if ( newS.startsWith( '{' ) )
|
||||
{
|
||||
sl.push_back( newS );
|
||||
}
|
||||
else
|
||||
{
|
||||
newS.replace( '\\', QStringLiteral( R"(\\)" ) );
|
||||
newS.replace( '\"', QStringLiteral( R"(\")" ) );
|
||||
sl.push_back( "\"" + newS + "\"" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -42,12 +42,14 @@ namespace QgsArrayUtils
|
||||
CORE_EXPORT QVariantList parse( const QString &string );
|
||||
|
||||
/**
|
||||
* Build a hstore-formatted string from a QVariantMap.
|
||||
* \param map The map to format as a string
|
||||
* Build a postgres array like formatted list in a string from a QVariantList
|
||||
* \param list The list that needs to be stored to the string
|
||||
* \since QGIS 3.8
|
||||
*/
|
||||
CORE_EXPORT QString build( const QVariantList &list );
|
||||
|
||||
QString getNextString( const QString &txt, int &i, const QString &sep );
|
||||
|
||||
};
|
||||
|
||||
#endif //QGSARRAYUTILS_H
|
||||
|
@ -22,12 +22,14 @@ class TestQgsArrayUtils : public QObject
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
|
||||
void testListToPgArrayStringAndBack();
|
||||
void testFallbackPgArrayStringToList();
|
||||
void testPgArrayStringToListAndBack();
|
||||
void testUnquotedPgArrayStringToListAndBack();
|
||||
void testNumberArrayStringToListAndBack();
|
||||
void testMultidimensionalPgArrayStringToListAndBack();
|
||||
};
|
||||
|
||||
|
||||
void TestQgsArrayUtils::testListToPgArrayStringAndBack()
|
||||
void TestQgsArrayUtils::testPgArrayStringToListAndBack()
|
||||
{
|
||||
|
||||
QVariantList vl;
|
||||
@ -41,14 +43,13 @@ void TestQgsArrayUtils::testListToPgArrayStringAndBack()
|
||||
vl.push_back( QStringLiteral( "...all the etceteras" ) );
|
||||
|
||||
QString string = QStringLiteral( "{\"one\",\"}two{\",\"thr\\\"ee\",\"fo,ur\",\"fiv'e\",6,\"and 7garcìa][\",\"...all the etceteras\"}" );
|
||||
|
||||
QCOMPARE( QgsArrayUtils::build( vl ), string );
|
||||
QCOMPARE( QgsArrayUtils::parse( string ), vl );
|
||||
|
||||
// and back
|
||||
QCOMPARE( QgsArrayUtils::parse( string ), vl );
|
||||
QCOMPARE( QgsArrayUtils::build( vl ), string );
|
||||
}
|
||||
|
||||
void TestQgsArrayUtils::testFallbackPgArrayStringToList()
|
||||
void TestQgsArrayUtils::testUnquotedPgArrayStringToListAndBack()
|
||||
{
|
||||
//there might have been used
|
||||
QVariantList vl;
|
||||
@ -60,13 +61,43 @@ void TestQgsArrayUtils::testFallbackPgArrayStringToList()
|
||||
vl.push_back( QStringLiteral( "that genius" ) );
|
||||
|
||||
QString fallback_string = QStringLiteral( "{one,two,three,four,five,that genius}" );
|
||||
|
||||
QCOMPARE( QgsArrayUtils::parse( fallback_string ), vl );
|
||||
|
||||
// and back with quotes
|
||||
// and back including quotes
|
||||
QString new_string = QStringLiteral( "{\"one\",\"two\",\"three\",\"four\",\"five\",\"that genius\"}" );
|
||||
QCOMPARE( QgsArrayUtils::build( vl ), new_string );
|
||||
}
|
||||
|
||||
void TestQgsArrayUtils::testNumberArrayStringToListAndBack()
|
||||
{
|
||||
//there might have been used
|
||||
QVariantList vl;
|
||||
vl.push_back( 1 );
|
||||
vl.push_back( 2 );
|
||||
vl.push_back( 3 );
|
||||
vl.push_back( 4 );
|
||||
|
||||
QString number_string = QStringLiteral( "{1,2,3,4}" );
|
||||
QCOMPARE( QgsArrayUtils::parse( number_string ), vl );
|
||||
|
||||
// and back without quotes
|
||||
QCOMPARE( QgsArrayUtils::build( vl ), number_string );
|
||||
}
|
||||
|
||||
void TestQgsArrayUtils::testMultidimensionalPgArrayStringToListAndBack()
|
||||
{
|
||||
//there might have been used
|
||||
QVariantList vl;
|
||||
vl.push_back( QStringLiteral( "{one one third,one two third,one three third}" ) );
|
||||
vl.push_back( QStringLiteral( "{\"two one third\",\"two two third\",\"two three third\"}" ) );
|
||||
vl.push_back( QStringLiteral( "{three one third,three two third,three three third}" ) );
|
||||
|
||||
QString string = QStringLiteral( "{{one one third,one two third,one three third},{\"two one third\",\"two two third\",\"two three third\"},{three one third,three two third,three three third}}" );
|
||||
QCOMPARE( QgsArrayUtils::parse( string ), vl );
|
||||
|
||||
// and back without quotes
|
||||
QCOMPARE( QgsArrayUtils::build( vl ), string );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsArrayUtils )
|
||||
#include "testqgsarrayutils.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user