[Development] [RFO QTBUG-84739] QJsonValue::fromVariant containing QByteArray

Tor Arne Vestbø Tor.arne.Vestbo at qt.io
Thu Jul 2 00:29:33 CEST 2020


Hi,

I can sympathize with the reporter that this is a quite serious regression from the user’s point of view, but I also see your point about the new behavior not being lossy.

Would it be an alternative to add an argument to QJsonValue::fromVariant e.g. that defaults to the old, unsurprising behavior, but allows opting in to the safer approach?

I assume people who put “true” binary data into their QByteArrays and convert that to JSON would have pre-base64-encoded it manually already, to avoid the data loss, so this is a surprising change for those that just use it as a poor man’s string. 

That the documentation says "a conversion to a QString will be attempted.” reads to me as being a conversion that is as close to the original as possible, and results in a string. So while the new behavior is technically a string, I can see how it’s surprising that it’s not exactly close to the original ascii data e.g. 😊

My 2øre

Tor Arne 

> On 1 Jul 2020, at 23:21, Thiago Macieira <thiago.macieira at intel.com> wrote:
> 
> Re: https://bugreports.qt.io/browse/QTBUG-84739
> Summary: Qt 5.15 has an unintentional change in behaviour that has broken 
> existing applications. We need to decide whether to:
> a) leave as-is
> b) revert permanently
> c) revert for 5.15.x but keep the new behaviour in 6.x
> 
> (a) implies one change in behaviour (5.14.2 to 5.15.0)
> (b) implies two changes in behaviour (to and from 5.15.0)
> (c) implies three changes in behaviour: 5.14.2→5.15.0, 5.15.0→5.15.1, 
> 5.15.x→6.0
> 
> Testcase:
>    const char binarydata[] = "\0\1\2\3\xff";
>    QVariantMap vmap = {
>        {"ascii", QByteArray("ASCII text.\n")},
>        {"latin1", QByteArray("R\xe9sum\xe9")},
>        {"utf8", QByteArray("Résumé")},
>        {"binary", QByteArray(binarydata, sizeof(binarydata) - 1)}
>    };
>    QJsonObject jobj = QJsonObject::fromVariantMap(vmap);
>    printf("%s", qPrintable(QJsonDocument(jobj).toJson()));
> 
> Because of the switch in QJsonValue/Object/Array to use the CBOR classes as a 
> backend, we had an uninteded change in behaviour in how the JSON 
> fromVariantXxx functions behave for some variant types that are not part of 
> the JSON specification. In the bug report, this happened specifically for 
> QByteArray.
> 
> Qt 5.14 output:
> {
>    "ascii": "ASCII text.\n",
>    "binary": null,
>    "latin1": "R�sum�",
>    "utf8": "Résumé"
> }
> 
> Qt 5.15 output:
> {
>    "ascii": "QVNDSUkgdGV4dC4K",
>    "binary": "AAECA_8",
>    "latin1": "UulzdW3p",
>    "utf8": "UsOpc3Vtw6k"
> }
> 
> The Qt 5.15 output is the Base64url encoding of the original byte array data, 
> following the CBOR recommendations on how to convert CBOR to JSON (RFC 7049 
> section 4.1[1]). This was unintentional and happened only due to code reuse. 
> The QtJson unit tests did not test this and do not test how converting from 
> any QVariant types except the base ones that apply to JSON, plus QUrl and 
> QUuid. But it has broken user code that relied on this feature.
> 
> It wasn't entirely undocumented. QJsonValue::fromVariant said:
>    For all other QVariant types a conversion to a QString will be attempted.
>    If the returned string
>    is empty, a Null QJsonValue will be stored, otherwise a String value using
>    the returned QString.
> [this text is changed in https://codereview.qt-project.org/305996]
> 
> Since it depends on QVariant::toString(), it's implicit that the behaviour can 
> change from version to version. For Qt 6.0, we're already discussing whether 
> conversion between types in QVariant makes sense and toString() is one of 
> those cases.
> 
> As can be seen in the testcase, the old behaviour is silently lossy, which 
> means it's dangerous. The new behaviour is surprising, but it's not lossy, as 
> the original byte data is retrievable:
> 
> 	$ base64 -d <<<QVNDSUkgdGV4dC4K 
> 	ASCII text.
> 	$ base64 -d <<<UulzdW3p | od -tc
> 	0000000   R 351   s   u   m 351
> 	$ base64 -d <<<UsOpc3Vtw6k=
> 	Résumé                                                                                                                                          
> 
> (the type change is inevitable and is accepted by the user)
> 
> Request For Opinion: what do we do now? See options at the beginning of the 
> email.
> 
> Note also this behaviour is not completely correct, because fromVariant 
> converts *empty* strings to null, but it should only convert null QStrings to 
> null, leaving empty strings alone (that's the QCborValue::fromVariant 
> behaviour). I also don't think the implementation is self-consistent, because 
> the conversion code appears more than once.
> 
> [1] https://tools.ietf.org/html/rfc7049#section-4.1
> -- 
> Thiago Macieira - thiago.macieira (AT) intel.com
>  Software Architect - Intel System Software Products
> 
> 
> 
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> https://lists.qt-project.org/listinfo/development



More information about the Development mailing list