summaryrefslogtreecommitdiffstats
path: root/examples/corelib/serialization
diff options
context:
space:
mode:
Diffstat (limited to 'examples/corelib/serialization')
-rw-r--r--examples/corelib/serialization/cbordump/cbordump.pro14
-rw-r--r--examples/corelib/serialization/cbordump/main.cpp765
-rw-r--r--examples/corelib/serialization/cbordump/tag-transform.xslt25
-rw-r--r--examples/corelib/serialization/convert/cborconverter.cpp393
-rw-r--r--examples/corelib/serialization/convert/cborconverter.h85
-rw-r--r--examples/corelib/serialization/convert/convert.pro29
-rw-r--r--examples/corelib/serialization/convert/converter.h103
-rw-r--r--examples/corelib/serialization/convert/datastreamconverter.cpp273
-rw-r--r--examples/corelib/serialization/convert/datastreamconverter.h85
-rw-r--r--examples/corelib/serialization/convert/jsonconverter.cpp212
-rw-r--r--examples/corelib/serialization/convert/jsonconverter.h85
-rw-r--r--examples/corelib/serialization/convert/main.cpp234
-rw-r--r--examples/corelib/serialization/convert/nullconverter.cpp99
-rw-r--r--examples/corelib/serialization/convert/nullconverter.h69
-rw-r--r--examples/corelib/serialization/convert/textconverter.cpp160
-rw-r--r--examples/corelib/serialization/convert/textconverter.h70
-rw-r--r--examples/corelib/serialization/convert/xmlconverter.cpp514
-rw-r--r--examples/corelib/serialization/convert/xmlconverter.h69
-rw-r--r--examples/corelib/serialization/serialization.pro5
19 files changed, 3288 insertions, 1 deletions
diff --git a/examples/corelib/serialization/cbordump/cbordump.pro b/examples/corelib/serialization/cbordump/cbordump.pro
new file mode 100644
index 0000000000..7fb2ef69f0
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/cbordump.pro
@@ -0,0 +1,14 @@
+QT += core
+QT -= gui
+
+TARGET = cbordump
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/cbordump
+INSTALLS += target
+
+SOURCES += main.cpp
diff --git a/examples/corelib/serialization/cbordump/main.cpp b/examples/corelib/serialization/cbordump/main.cpp
new file mode 100644
index 0000000000..222bd43645
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/main.cpp
@@ -0,0 +1,765 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCborStreamReader>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QCoreApplication>
+#include <QFile>
+#include <QLocale>
+#include <QStack>
+
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/*
+ * To regenerate:
+ * curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
+ * xsltproc tag-transform.xslt cbor-tags.xml
+ */
+
+// GENERATED CODE
+struct CborTagDescription
+{
+ QCborTag tag;
+ const char *description; // with space and parentheses
+};
+
+// CBOR Tags
+static const CborTagDescription tagDescriptions[] = {
+ // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+ { QCborTag(0), " (Standard date/time string; see Section 2.4.1 [RFC7049])" },
+ { QCborTag(1), " (Epoch-based date/time; see Section 2.4.1 [RFC7049])" },
+ { QCborTag(2), " (Positive bignum; see Section 2.4.2 [RFC7049])" },
+ { QCborTag(3), " (Negative bignum; see Section 2.4.2 [RFC7049])" },
+ { QCborTag(4), " (Decimal fraction; see Section 2.4.3 [RFC7049])" },
+ { QCborTag(5), " (Bigfloat; see Section 2.4.3 [RFC7049])" },
+ { QCborTag(16), " (COSE Single Recipient Encrypted Data Object [RFC8152])" },
+ { QCborTag(17), " (COSE Mac w/o Recipients Object [RFC8152])" },
+ { QCborTag(18), " (COSE Single Signer Data Object [RFC8152])" },
+ { QCborTag(21), " (Expected conversion to base64url encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(22), " (Expected conversion to base64 encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(23), " (Expected conversion to base16 encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(24), " (Encoded CBOR data item; see Section 2.4.4.1 [RFC7049])" },
+ { QCborTag(25), " (reference the nth previously seen string)" },
+ { QCborTag(26), " (Serialised Perl object with classname and constructor arguments)" },
+ { QCborTag(27), " (Serialised language-independent object with type name and constructor arguments)" },
+ { QCborTag(28), " (mark value as (potentially) shared)" },
+ { QCborTag(29), " (reference nth marked value)" },
+ { QCborTag(30), " (Rational number)" },
+ { QCborTag(32), " (URI; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(33), " (base64url; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(34), " (base64; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(35), " (Regular expression; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(36), " (MIME message; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(37), " (Binary UUID ( section 4.1.2))" },
+ { QCborTag(38), " (Language-tagged string)" },
+ { QCborTag(39), " (Identifier)" },
+ { QCborTag(61), " (CBOR Web Token (CWT))" },
+ { QCborTag(96), " (COSE Encrypted Data Object [RFC8152])" },
+ { QCborTag(97), " (COSE MACed Data Object [RFC8152])" },
+ { QCborTag(98), " (COSE Signed Data Object [RFC8152])" },
+ { QCborTag(256), " (mark value as having string references)" },
+ { QCborTag(257), " (Binary MIME message)" },
+ { QCborTag(258), " (Mathematical finite set)" },
+ { QCborTag(260), " (Network Address (IPv4 or IPv6 or MAC Address))" },
+ { QCborTag(264), " (Decimal fraction with arbitrary exponent)" },
+ { QCborTag(265), " (Bigfloat with arbitrary exponent)" },
+ { QCborTag(1001), " (extended time)" },
+ { QCborTag(1002), " (duration)" },
+ { QCborTag(1003), " (period)" },
+ { QCborTag(22098), " (hint that indicates an additional level of indirection)" },
+ { QCborTag(55799), " (Self-describe CBOR; see Section 2.4.5 [RFC7049])" },
+ { QCborTag(15309736), " (RAINS Message)" },
+ { QCborTag(-1), nullptr }
+};
+// END GENERATED CODE
+
+enum {
+ // See RFC 7049 section 2.
+ SmallValueBitLength = 5,
+ SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
+ Value8Bit = 24,
+ Value16Bit = 25,
+ Value32Bit = 26,
+ Value64Bit = 27
+};
+
+struct CborDumper
+{
+ enum DumpOption {
+ ShowCompact = 0x01,
+ ShowWidthIndicators = 0x02,
+ ShowAnnotated = 0x04
+ };
+ Q_DECLARE_FLAGS(DumpOptions, DumpOption)
+
+ CborDumper(QFile *f, DumpOptions opts_);
+ QCborError dump();
+
+private:
+ void dumpOne(int nestingLevel);
+ void dumpOneDetailed(int nestingLevel);
+
+ void printByteArray(const QByteArray &ba);
+ void printWidthIndicator(quint64 value, char space = '\0');
+ void printStringWidthIndicator(quint64 value);
+
+ QCborStreamReader reader;
+ QByteArray data;
+ QStack<quint8> byteArrayEncoding;
+ qint64 offset = 0;
+ DumpOptions opts;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(CborDumper::DumpOptions)
+
+static int cborNumberSize(quint64 value)
+{
+ int normalSize = 1;
+ if (value > std::numeric_limits<quint32>::max())
+ normalSize += 8;
+ else if (value > std::numeric_limits<quint16>::max())
+ normalSize += 4;
+ else if (value > std::numeric_limits<quint8>::max())
+ normalSize += 2;
+ else if (value >= Value8Bit)
+ normalSize += 1;
+ return normalSize;
+}
+
+CborDumper::CborDumper(QFile *f, DumpOptions opts_)
+ : opts(opts_)
+{
+ // try to mmap the file, this is faster
+ char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption));
+ if (ptr) {
+ // worked
+ data = QByteArray::fromRawData(ptr, f->size());
+ reader.addData(data);
+ } else if ((opts & ShowAnnotated) || f->isSequential()) {
+ // details requires full contents, so allocate memory
+ data = f->readAll();
+ reader.addData(data);
+ } else {
+ // just use the QIODevice
+ reader.setDevice(f);
+ }
+}
+
+QCborError CborDumper::dump()
+{
+ byteArrayEncoding << quint8(QCborKnownTags::ExpectedBase16);
+ if (!reader.lastError()) {
+ if (opts & ShowAnnotated)
+ dumpOneDetailed(0);
+ else
+ dumpOne(0);
+ }
+
+ QCborError err = reader.lastError();
+ offset = reader.currentOffset();
+ if (err) {
+ fflush(stdout);
+ fprintf(stderr, "cbordump: decoding failed at %lld: %s\n",
+ offset, qPrintable(err.toString()));
+ if (!data.isEmpty())
+ fprintf(stderr, " bytes at %lld: %s\n", offset,
+ data.mid(offset, 9).toHex(' ').constData());
+ } else {
+ if (!opts.testFlag(ShowAnnotated))
+ printf("\n");
+ if (offset < data.size() || (reader.device() && reader.device()->bytesAvailable()))
+ fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
+ }
+
+ return err;
+}
+
+template <typename T> static inline bool canConvertTo(double v)
+{
+ // The [conv.fpint] (7.10 Floating-integral conversions) section of the
+ // standard says only exact conversions are guaranteed. Converting
+ // integrals to floating-point with loss of precision has implementation-
+ // defined behavior whether the next higher or next lower is returned;
+ // converting FP to integral is UB if it can't be represented.;
+ Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer);
+
+ double supremum = ldexp(1, std::numeric_limits<T>::digits);
+ if (v >= supremum)
+ return false;
+
+ if (v < std::numeric_limits<T>::min()) // either zero or a power of two, so it's exact
+ return false;
+
+ // we're in range
+ return v == floor(v);
+}
+
+static QString fpToString(double v, const char *suffix)
+{
+ if (qIsInf(v))
+ return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
+ if (qIsNaN(v))
+ return QStringLiteral("nan");
+ if (canConvertTo<qint64>(v))
+ return QString::number(qint64(v)) + ".0" + suffix;
+ if (canConvertTo<quint64>(v))
+ return QString::number(quint64(v)) + ".0" + suffix;
+
+ QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
+ if (!s.contains('.') && !s.contains('e'))
+ s += '.';
+ s += suffix;
+ return s;
+};
+
+void CborDumper::dumpOne(int nestingLevel)
+{
+ QString indent(1, QLatin1Char(' '));
+ QString indented = indent;
+ if (!opts.testFlag(ShowCompact)) {
+ indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
+ indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
+ }
+
+ switch (reader.type()) {
+ case QCborStreamReader::UnsignedInteger: {
+ quint64 u = reader.toUnsignedInteger();
+ printf("%llu", u);
+ reader.next();
+ printWidthIndicator(u);
+ return;
+ }
+
+ case QCborStreamReader::NegativeInteger: {
+ quint64 n = quint64(reader.toNegativeInteger());
+ if (n == 0) // -2^64 (wrapped around)
+ printf("-18446744073709551616");
+ else
+ printf("-%llu", n);
+ reader.next();
+ printWidthIndicator(n);
+ return;
+ }
+
+ case QCborStreamReader::ByteArray:
+ case QCborStreamReader::String: {
+ bool isLengthKnown = reader.isLengthKnown();
+ if (!isLengthKnown) {
+ printf("(_ ");
+ ++offset;
+ }
+
+ QString comma;
+ if (reader.isByteArray()) {
+ auto r = reader.readByteArray();
+ while (r.status == QCborStreamReader::Ok) {
+ printf("%s", qPrintable(comma));
+ printByteArray(r.data);
+ printStringWidthIndicator(r.data.size());
+
+ r = reader.readByteArray();
+ comma = QLatin1Char(',') + indented;
+ }
+ } else {
+ auto r = reader.readString();
+ while (r.status == QCborStreamReader::Ok) {
+ printf("%s\"%s\"", qPrintable(comma), qPrintable(r.data));
+ printStringWidthIndicator(r.data.toUtf8().size());
+
+ r = reader.readString();
+ comma = QLatin1Char(',') + indented;
+ }
+ }
+
+ if (!isLengthKnown && !reader.lastError())
+ printf(")");
+ break;
+ }
+
+ case QCborStreamReader::Array:
+ case QCborStreamReader::Map: {
+ const char *delimiters = (reader.isArray() ? "[]" : "{}");
+ printf("%c", delimiters[0]);
+
+ if (reader.isLengthKnown()) {
+ quint64 len = reader.length();
+ reader.enterContainer();
+ printWidthIndicator(len, ' ');
+ } else {
+ reader.enterContainer();
+ offset = reader.currentOffset();
+ printf("_ ");
+ }
+
+ const char *comma = "";
+ while (!reader.lastError() && reader.hasNext()) {
+ printf("%s%s", comma, qPrintable(indented));
+ comma = ",";
+ dumpOne(nestingLevel + 1);
+
+ if (reader.parentContainerType() != QCborStreamReader::Map)
+ continue;
+ if (reader.lastError())
+ break;
+ printf(": ");
+ dumpOne(nestingLevel + 1);
+ }
+
+ if (!reader.lastError()) {
+ reader.leaveContainer();
+ printf("%s%c", qPrintable(indent), delimiters[1]);
+ }
+ break;
+ }
+
+ case QCborStreamReader::Tag: {
+ QCborTag tag = reader.toTag();
+ printf("%llu", quint64(tag));
+
+ if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
+ || tag == QCborKnownTags::ExpectedBase64url)
+ byteArrayEncoding.push(quint8(tag));
+
+ if (reader.next()) {
+ printWidthIndicator(quint64(tag));
+ printf("(");
+ dumpOne(nestingLevel); // same level!
+ printf(")");
+ }
+
+ if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
+ || tag == QCborKnownTags::ExpectedBase64url)
+ byteArrayEncoding.pop();
+ break;
+ }
+
+ case QCborStreamReader::SimpleType:
+ switch (reader.toSimpleType()) {
+ case QCborSimpleType::False:
+ printf("false");
+ break;
+ case QCborSimpleType::True:
+ printf("true");
+ break;
+ case QCborSimpleType::Null:
+ printf("null");
+ break;
+ case QCborSimpleType::Undefined:
+ printf("undefined");
+ break;
+ default:
+ printf("simple(%u)", quint8(reader.toSimpleType()));
+ break;
+ }
+ reader.next();
+ break;
+
+ case QCborStreamReader::Float16:
+ printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
+ reader.next();
+ break;
+ case QCborStreamReader::Float:
+ printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
+ reader.next();
+ break;
+ case QCborStreamReader::Double:
+ printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
+ reader.next();
+ break;
+ case QCborStreamReader::Invalid:
+ return;
+ }
+
+ offset = reader.currentOffset();
+}
+
+void CborDumper::dumpOneDetailed(int nestingLevel)
+{
+ auto tagDescription = [](QCborTag tag) {
+ for (auto entry : tagDescriptions) {
+ if (entry.tag == tag)
+ return entry.description;
+ if (entry.tag > tag)
+ break;
+ }
+ return "";
+ };
+ auto printOverlong = [](int actualSize, quint64 value) {
+ if (cborNumberSize(value) != actualSize)
+ printf(" (overlong)");
+ };
+ auto print = [=](const char *descr, const char *fmt, ...) {
+ qint64 prevOffset = offset;
+ offset = reader.currentOffset();
+ if (prevOffset == offset)
+ return;
+
+ QByteArray bytes = data.mid(prevOffset, offset - prevOffset);
+ QByteArray indent(nestingLevel * 2, ' ');
+ printf("%-50s # %s ", (indent + bytes.toHex(' ')).constData(), descr);
+
+ va_list va;
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ va_end(va);
+
+ if (strstr(fmt, "%ll")) {
+ // Only works because all callers below that use %ll, use it as the
+ // first arg
+ va_start(va, fmt);
+ quint64 value = va_arg(va, quint64);
+ va_end(va);
+ printOverlong(bytes.size(), value);
+ }
+
+ puts("");
+ };
+
+ auto printFp = [=](const char *descr, double d) {
+ QString s = fpToString(d, "");
+ if (s.size() <= 6)
+ return print(descr, "%s", qPrintable(s));
+ return print(descr, "%a", d);
+ };
+
+ auto printString = [=](const char *descr) {
+ QByteArray indent(nestingLevel * 2, ' ');
+ const char *chunkStr = (reader.isLengthKnown() ? "" : "chunk ");
+ int width = 48 - indent.size();
+ int bytesPerLine = qMax(width / 3, 5);
+
+ qsizetype size = reader.currentStringChunkSize();
+ if (size < 0)
+ return; // error
+ if (size >= std::numeric_limits<int>::max()) {
+ fprintf(stderr, "String length too big, %lli\n", qint64(size));
+ exit(EXIT_FAILURE);
+ }
+
+ // if asking for the current string chunk changes the offset, then it
+ // was chunked
+ print(descr, "(indeterminate length)");
+
+ QByteArray bytes(size, Qt::Uninitialized);
+ auto r = reader.readStringChunk(bytes.data(), bytes.size());
+ while (r.status == QCborStreamReader::Ok) {
+ // We'll have to decode the length's width directly from CBOR...
+ const char *lenstart = data.constData() + offset;
+ const char *lenend = lenstart + 1;
+ quint8 additionalInformation = (*lenstart & SmallValueMask);
+
+ // Decode this number directly from CBOR (see RFC 7049 section 2)
+ if (additionalInformation >= Value8Bit) {
+ if (additionalInformation == Value8Bit)
+ lenend += 1;
+ else if (additionalInformation == Value16Bit)
+ lenend += 2;
+ else if (additionalInformation == Value32Bit)
+ lenend += 4;
+ else
+ lenend += 8;
+ }
+
+ {
+ QByteArray lenbytes = QByteArray::fromRawData(lenstart, lenend - lenstart);
+ printf("%-50s # %s %slength %llu",
+ (indent + lenbytes.toHex(' ')).constData(), descr, chunkStr, quint64(size));
+ printOverlong(lenbytes.size(), size);
+ puts("");
+ }
+
+ offset = reader.currentOffset();
+
+ for (int i = 0; i < r.data; i += bytesPerLine) {
+ QByteArray section = bytes.mid(i, bytesPerLine);
+ printf(" %s%s", indent.constData(), section.toHex(' ').constData());
+
+ // print the decode
+ QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
+ printf("%s # \"", spaces.constData());
+ auto ptr = reinterpret_cast<const uchar *>(section.constData());
+ for (int j = 0; j < section.size(); ++j)
+ printf("%c", ptr[j] >= 0x80 || ptr[j] < 0x20 ? '.' : ptr[j]);
+
+ puts("\"");
+ }
+
+ // get the next chunk
+ size = reader.currentStringChunkSize();
+ if (size < 0)
+ return; // error
+ if (size >= std::numeric_limits<int>::max()) {
+ fprintf(stderr, "String length too big, %lli\n", qint64(size));
+ exit(EXIT_FAILURE);
+ }
+ bytes.resize(size);
+ r = reader.readStringChunk(bytes.data(), bytes.size());
+ }
+ };
+
+ if (reader.lastError())
+ return;
+
+ switch (reader.type()) {
+ case QCborStreamReader::UnsignedInteger: {
+ quint64 u = reader.toUnsignedInteger();
+ reader.next();
+ if (u < 65536 || (u % 100000) == 0)
+ print("Unsigned integer", "%llu", u);
+ else
+ print("Unsigned integer", "0x%llx", u);
+ return;
+ }
+
+ case QCborStreamReader::NegativeInteger: {
+ quint64 n = quint64(reader.toNegativeInteger());
+ reader.next();
+ print("Negative integer", n == 0 ? "-18446744073709551616" : "-%llu", n);
+ return;
+ }
+
+ case QCborStreamReader::ByteArray:
+ case QCborStreamReader::String: {
+ bool isLengthKnown = reader.isLengthKnown();
+ const char *descr = (reader.isString() ? "Text string" : "Byte string");
+ if (!isLengthKnown)
+ ++nestingLevel;
+
+ printString(descr);
+ if (reader.lastError())
+ return;
+
+ if (!isLengthKnown) {
+ --nestingLevel;
+ print("Break", "");
+ }
+ break;
+ }
+
+ case QCborStreamReader::Array:
+ case QCborStreamReader::Map: {
+ const char *descr = (reader.isArray() ? "Array" : "Map");
+ if (reader.isLengthKnown()) {
+ quint64 len = reader.length();
+ reader.enterContainer();
+ print(descr, "length %llu", len);
+ } else {
+ reader.enterContainer();
+ print(descr, "(indeterminate length)");
+ }
+
+ while (!reader.lastError() && reader.hasNext())
+ dumpOneDetailed(nestingLevel + 1);
+
+ if (!reader.lastError()) {
+ reader.leaveContainer();
+ print("Break", "");
+ }
+ break;
+ }
+
+ case QCborStreamReader::Tag: {
+ QCborTag tag = reader.toTag();
+ reader.next();
+ print("Tag", "%llu%s", quint64(tag), tagDescription(tag));
+ dumpOneDetailed(nestingLevel + 1);
+ break;
+ }
+
+ case QCborStreamReader::SimpleType: {
+ QCborSimpleType st = reader.toSimpleType();
+ reader.next();
+ switch (st) {
+ case QCborSimpleType::False:
+ print("Simple Type", "false");
+ break;
+ case QCborSimpleType::True:
+ print("Simple Type", "true");
+ break;
+ case QCborSimpleType::Null:
+ print("Simple Type", "null");
+ break;
+ case QCborSimpleType::Undefined:
+ print("Simple Type", "undefined");
+ break;
+ default:
+ print("Simple Type", "%u", quint8(st));
+ break;
+ }
+ break;
+ }
+
+ case QCborStreamReader::Float16: {
+ double d = reader.toFloat16();
+ reader.next();
+ printFp("Float16", d);
+ break;
+ }
+ case QCborStreamReader::Float: {
+ double d = reader.toFloat();
+ reader.next();
+ printFp("Float", d);
+ break;
+ }
+ case QCborStreamReader::Double: {
+ double d = reader.toDouble();
+ reader.next();
+ printFp("Double", d);
+ break;
+ }
+ case QCborStreamReader::Invalid:
+ return;
+ }
+
+ offset = reader.currentOffset();
+}
+
+void CborDumper::printByteArray(const QByteArray &ba)
+{
+ switch (byteArrayEncoding.top()) {
+ default:
+ printf("h'%s'", ba.toHex(' ').constData());
+ break;
+
+ case quint8(QCborKnownTags::ExpectedBase64):
+ printf("b64'%s'", ba.toBase64().constData());
+ break;
+
+ case quint8(QCborKnownTags::ExpectedBase64url):
+ printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
+ break;
+ }
+}
+
+void printIndicator(quint64 value, qint64 previousOffset, qint64 offset, char space)
+{
+ int normalSize = cborNumberSize(value);
+ int actualSize = offset - previousOffset;
+
+ if (actualSize != normalSize) {
+ Q_ASSERT(actualSize > 1);
+ actualSize -= 2;
+ printf("_%d", qPopulationCount(uint(actualSize)));
+ if (space)
+ printf("%c", space);
+ }
+}
+
+void CborDumper::printWidthIndicator(quint64 value, char space)
+{
+ qint64 previousOffset = offset;
+ offset = reader.currentOffset();
+ if (opts & ShowWidthIndicators)
+ printIndicator(value, previousOffset, offset, space);
+}
+
+void CborDumper::printStringWidthIndicator(quint64 value)
+{
+ qint64 previousOffset = offset;
+ offset = reader.currentOffset();
+ if (opts & ShowWidthIndicators)
+ printIndicator(value, previousOffset, offset - uint(value), '\0');
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ setlocale(LC_ALL, "C");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
+ parser.addHelpOption();
+
+ QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
+ QStringLiteral("Use compact form (no line breaks)"));
+ parser.addOption(compact);
+
+ QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
+ QStringLiteral("Show indicators for width of lengths and integrals"));
+ parser.addOption(showIndicators);
+
+ QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
+ QStringLiteral("Show bytes and annotated decoding"));
+ parser.addOption(verbose);
+
+ parser.addPositionalArgument(QStringLiteral("[source]"),
+ QStringLiteral("CBOR file to read from"));
+
+ parser.process(app);
+
+ CborDumper::DumpOptions opts;
+ if (parser.isSet(compact))
+ opts |= CborDumper::ShowCompact;
+ if (parser.isSet(showIndicators))
+ opts |= CborDumper::ShowWidthIndicators;
+ if (parser.isSet(verbose))
+ opts |= CborDumper::ShowAnnotated;
+
+ QStringList files = parser.positionalArguments();
+ if (files.isEmpty())
+ files << "-";
+ for (const QString &file : qAsConst(files)) {
+ QFile f(file);
+ if (file == "-" ? f.open(stdin, QIODevice::ReadOnly) : f.open(QIODevice::ReadOnly)) {
+ if (files.size() > 1)
+ printf("/ From \"%s\" /\n", qPrintable(file));
+
+ CborDumper dumper(&f, opts);
+ QCborError err = dumper.dump();
+ if (err)
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/corelib/serialization/cbordump/tag-transform.xslt b/examples/corelib/serialization/cbordump/tag-transform.xslt
new file mode 100644
index 0000000000..3cc1b9b293
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/tag-transform.xslt
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.iana.org/assignments" xmlns="http://www.iana.org/assignments" xmlns:_="http://www.iana.org/assignments" xmlns:DEFAULT="http://www.iana.org/assignments" version="1.0">
+<xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
+<xsl:template match="/a:registry[@id='cbor-tags']">struct CborTagDescription
+{
+ QCborTag tag;
+ const char *description; // with space and parentheses
+};
+
+// <xsl:value-of select="a:registry/a:title"/>
+static const CborTagDescription tagDescriptions[] = {
+ // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+<xsl:for-each select="a:registry/a:record">
+ <xsl:sort select="a:value" data-type="number"/>
+ <xsl:if test="a:semantics != ''">
+ <xsl:call-template name="row"/>
+ </xsl:if>
+ </xsl:for-each> { QCborTag(-1), nullptr }
+};
+</xsl:template>
+<xsl:template name="row"> { QCborTag(<xsl:value-of select="a:value"/>), " (<xsl:value-of select="a:semantics"/> <xsl:call-template name="xref"/>)" },
+</xsl:template>
+<xsl:template name="xref"><xsl:if test="a:xref/@type = 'rfc'"> [<xsl:value-of select="translate(a:xref/@data,'rfc','RFC')"/>]</xsl:if>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/examples/corelib/serialization/convert/cborconverter.cpp b/examples/corelib/serialization/convert/cborconverter.cpp
new file mode 100644
index 0000000000..41724c935e
--- /dev/null
+++ b/examples/corelib/serialization/convert/cborconverter.cpp
@@ -0,0 +1,393 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "cborconverter.h"
+
+#include <QCborStreamReader>
+#include <QCborStreamWriter>
+#include <QCborMap>
+#include <QCborArray>
+#include <QCborValue>
+#include <QDataStream>
+#include <QDebug>
+#include <QFloat16>
+#include <QFile>
+#include <QMetaType>
+#include <QTextStream>
+
+#include <stdio.h>
+
+static CborConverter cborConverter;
+static CborDiagnosticDumper cborDiagnosticDumper;
+
+static const char optionHelp[] =
+ "convert-float-to-int=yes|no Write integers instead of floating point, if no\n"
+ " loss of precision occurs on conversion.\n"
+ "float16=yes|always|no Write using half-precision floating point.\n"
+ " If 'always', won't check for loss of precision.\n"
+ "float32=yes|always|no Write using single-precision floating point.\n"
+ " If 'always', won't check for loss of precision.\n"
+ "signature=yes|no Prepend the CBOR signature to the file output.\n"
+ ;
+
+static const char diagnosticHelp[] =
+ "extended=no|yes Use extended CBOR diagnostic format.\n"
+ "line-wrap=yes|no Split output into multiple lines.\n"
+ ;
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<<(QDataStream &ds, QCborSimpleType st)
+{
+ return ds << quint8(st);
+}
+
+QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st)
+{
+ quint8 v;
+ ds >> v;
+ st = QCborSimpleType(v);
+ return ds;
+}
+
+QDataStream &operator<<(QDataStream &ds, QCborTag tag)
+{
+ return ds << quint64(tag);
+}
+
+QDataStream &operator>>(QDataStream &ds, QCborTag &tag)
+{
+ quint64 v;
+ ds >> v;
+ tag = QCborTag(v);
+ return ds;
+}
+
+QT_END_NAMESPACE
+
+// We can't use QCborValue::toVariant directly because that would destroy
+// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we
+// have our own set of converter functions so we can keep the keys properly.
+
+static QVariant convertCborValue(const QCborValue &value);
+
+static QVariant convertCborMap(const QCborMap &map)
+{
+ VariantOrderedMap result;
+ result.reserve(map.size());
+ for (auto pair : map)
+ result.append({ convertCborValue(pair.first), convertCborValue(pair.second) });
+ return QVariant::fromValue(result);
+}
+
+static QVariant convertCborArray(const QCborArray &array)
+{
+ QVariantList result;
+ result.reserve(array.size());
+ for (auto value : array)
+ result.append(convertCborValue(value));
+ return result;
+}
+
+static QVariant convertCborValue(const QCborValue &value)
+{
+ if (value.isArray())
+ return convertCborArray(value.toArray());
+ if (value.isMap())
+ return convertCborMap(value.toMap());
+ return value.toVariant();
+}
+
+enum TrimFloatingPoint { Double, Float, Float16 };
+static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
+{
+ if (v.userType() == QVariant::List) {
+ const QVariantList list = v.toList();
+ QCborArray array;
+ for (const QVariant &v : list)
+ array.append(convertFromVariant(v, fpTrimming));
+
+ return array;
+ }
+
+ if (v.userType() == qMetaTypeId<VariantOrderedMap>()) {
+ const auto m = v.value<VariantOrderedMap>();
+ QCborMap map;
+ for (const auto &pair : m)
+ map.insert(convertFromVariant(pair.first, fpTrimming),
+ convertFromVariant(pair.second, fpTrimming));
+ return map;
+ }
+
+ if (v.userType() == QVariant::Double && fpTrimming != Double) {
+ float f = float(v.toDouble());
+ if (fpTrimming == Float16)
+ return float(qfloat16(f));
+ return f;
+ }
+
+ return QCborValue::fromVariant(v);
+}
+
+QString CborDiagnosticDumper::name()
+{
+ return QStringLiteral("cbor-dump");
+}
+
+Converter::Direction CborDiagnosticDumper::directions()
+{
+ return Out;
+}
+
+Converter::Options CborDiagnosticDumper::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *CborDiagnosticDumper::optionsHelp()
+{
+ return diagnosticHelp;
+}
+
+bool CborDiagnosticDumper::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNREACHABLE();
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ return QVariant();
+}
+
+void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped;
+ for (const QString &s : options) {
+ QStringList pair = s.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "line-wrap") {
+ opts &= ~QCborValue::LineWrapped;
+ if (pair.last() == "yes") {
+ opts |= QCborValue::LineWrapped;
+ continue;
+ } else if (pair.last() == "no") {
+ continue;
+ }
+ }
+ if (pair.first() == "extended") {
+ opts &= ~QCborValue::ExtendedFormat;
+ if (pair.last() == "yes")
+ opts |= QCborValue::ExtendedFormat;
+ continue;
+ }
+ }
+
+ fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
+ qPrintable(s), diagnosticHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ QTextStream out(f);
+ out << convertFromVariant(contents, Double).toDiagnosticNotation(opts)
+ << endl;
+}
+
+CborConverter::CborConverter()
+{
+ qRegisterMetaType<QCborTag>();
+ qRegisterMetaTypeStreamOperators<QCborTag>();
+ QMetaType::registerDebugStreamOperator<QCborTag>();
+}
+
+QString CborConverter::name()
+{
+ return "cbor";
+}
+
+Converter::Direction CborConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options CborConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *CborConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool CborConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".cbor")))
+ return true;
+ }
+ return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
+}
+
+QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ const char *ptr = nullptr;
+ if (auto file = qobject_cast<QFile *>(f))
+ ptr = reinterpret_cast<char *>(file->map(0, file->size()));
+
+ QByteArray mapped = QByteArray::fromRawData(ptr, ptr ? f->size() : 0);
+ QCborStreamReader reader(mapped);
+ if (!ptr)
+ reader.setDevice(f);
+
+ if (reader.isTag() && reader.toTag() == QCborKnownTags::Signature)
+ reader.next();
+
+ QCborValue contents = QCborValue::fromCbor(reader);
+ qint64 offset = reader.currentOffset();
+ if (reader.lastError()) {
+ fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset,
+ qPrintable(reader.lastError().toString()));
+ fprintf(stderr, " bytes: %s\n",
+ (ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData());
+ exit(EXIT_FAILURE);
+ } else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) {
+ fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
+ }
+
+ if (outputConverter == nullptr)
+ outputConverter = &cborDiagnosticDumper;
+ else if (outputConverter == null)
+ return QVariant();
+ else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
+ return contents.toVariant();
+ return convertCborValue(contents);
+}
+
+void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ bool useSignature = true;
+ bool useIntegers = true;
+ enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
+
+ for (const QString &s : options) {
+ QStringList pair = s.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "convert-float-to-int") {
+ if (pair.last() == "yes") {
+ useIntegers = true;
+ continue;
+ } else if (pair.last() == "no") {
+ useIntegers = false;
+ continue;
+ }
+ }
+
+ if (pair.first() == "float16") {
+ if (pair.last() == "no") {
+ useFloat16 = No;
+ continue;
+ } else if (pair.last() == "yes") {
+ useFloat16 = Yes;
+ continue;
+ } else if (pair.last() == "always") {
+ useFloat16 = Always;
+ continue;
+ }
+ }
+
+ if (pair.first() == "float32") {
+ if (pair.last() == "no") {
+ useFloat = No;
+ continue;
+ } else if (pair.last() == "yes") {
+ useFloat = Yes;
+ continue;
+ } else if (pair.last() == "always") {
+ useFloat = Always;
+ continue;
+ }
+ }
+
+ if (pair.first() == "signature") {
+ if (pair.last() == "yes") {
+ useSignature = true;
+ continue;
+ } else if (pair.last() == "no") {
+ useSignature = false;
+ continue;
+ }
+ }
+ }
+
+ fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s",
+ qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ QCborValue v = convertFromVariant(contents,
+ useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
+ QCborStreamWriter writer(f);
+ if (useSignature)
+ writer.append(QCborKnownTags::Signature);
+
+ QCborValue::EncodingOptions opts;
+ if (useIntegers)
+ opts |= QCborValue::UseIntegers;
+ if (useFloat != No)
+ opts |= QCborValue::UseFloat;
+ if (useFloat16 != No)
+ opts |= QCborValue::UseFloat16;
+ v.toCbor(writer, opts);
+}
+
diff --git a/examples/corelib/serialization/convert/cborconverter.h b/examples/corelib/serialization/convert/cborconverter.h
new file mode 100644
index 0000000000..f0a89cb141
--- /dev/null
+++ b/examples/corelib/serialization/convert/cborconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CBORCONVERTER_H
+#define CBORCONVERTER_H
+
+#include "converter.h"
+
+class CborDiagnosticDumper : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class CborConverter : public Converter
+{
+public:
+ CborConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // CBORCONVERTER_H
diff --git a/examples/corelib/serialization/convert/convert.pro b/examples/corelib/serialization/convert/convert.pro
new file mode 100644
index 0000000000..d9b1de41e3
--- /dev/null
+++ b/examples/corelib/serialization/convert/convert.pro
@@ -0,0 +1,29 @@
+QT += core
+QT -= gui
+
+TARGET = convert
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert
+INSTALLS += target
+
+SOURCES += main.cpp \
+ cborconverter.cpp \
+ jsonconverter.cpp \
+ datastreamconverter.cpp \
+ textconverter.cpp \
+ xmlconverter.cpp \
+ nullconverter.cpp
+
+HEADERS += \
+ converter.h \
+ cborconverter.h \
+ jsonconverter.h \
+ datastreamconverter.h \
+ textconverter.h \
+ xmlconverter.h \
+ nullconverter.h
diff --git a/examples/corelib/serialization/convert/converter.h b/examples/corelib/serialization/convert/converter.h
new file mode 100644
index 0000000000..82e1fa1cb3
--- /dev/null
+++ b/examples/corelib/serialization/convert/converter.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CONVERTER_H
+#define CONVERTER_H
+
+#include <QIODevice>
+#include <QPair>
+#include <QVariant>
+#include <QVector>
+
+class VariantOrderedMap : public QVector<QPair<QVariant, QVariant>>
+{
+public:
+ VariantOrderedMap() = default;
+ VariantOrderedMap(const QVariantMap &map)
+ {
+ reserve(map.size());
+ for (auto it = map.begin(); it != map.end(); ++it)
+ append({it.key(), it.value()});
+ }
+};
+using Map = VariantOrderedMap;
+Q_DECLARE_METATYPE(Map)
+
+class Converter
+{
+protected:
+ Converter();
+
+public:
+ static Converter *null;
+
+ enum Direction {
+ In = 1, Out = 2, InOut = 3
+ };
+
+ enum Option {
+ SupportsArbitraryMapKeys = 0x01
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ virtual ~Converter() = 0;
+
+ virtual QString name() = 0;
+ virtual Direction directions() = 0;
+ virtual Options outputOptions() = 0;
+ virtual const char *optionsHelp() = 0;
+ virtual bool probeFile(QIODevice *f) = 0;
+ virtual QVariant loadFile(QIODevice *f, Converter *&outputConverter) = 0;
+ virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
+
+#endif // CONVERTER_H
diff --git a/examples/corelib/serialization/convert/datastreamconverter.cpp b/examples/corelib/serialization/convert/datastreamconverter.cpp
new file mode 100644
index 0000000000..7cdb844141
--- /dev/null
+++ b/examples/corelib/serialization/convert/datastreamconverter.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "datastreamconverter.h"
+
+#include <QDataStream>
+#include <QDebug>
+#include <QTextStream>
+
+static const char optionHelp[] =
+ "byteorder=host|big|little Byte order to use.\n"
+ "version=<n> QDataStream version (default: Qt 5.0).\n"
+ ;
+
+static const char signature[] = "qds";
+
+static DataStreamDumper dataStreamDumper;
+static DataStreamConverter DataStreamConverter;
+
+QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map)
+{
+ ds << qint64(map.size());
+ for (const auto &pair : map)
+ ds << pair.first << pair.second;
+ return ds;
+}
+
+QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map)
+{
+ map.clear();
+
+ qint64 size;
+ ds >> size;
+ map.reserve(size);
+
+ while (size-- > 0) {
+ VariantOrderedMap::value_type pair;
+ ds >> pair.first >> pair.second;
+ map.append(pair);
+ }
+
+ return ds;
+}
+
+
+static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n"))
+{
+ QString result;
+ QString indented = indent + QLatin1String(" ");
+
+ int type = v.userType();
+ if (type == qMetaTypeId<VariantOrderedMap>() || type == QVariant::Map) {
+ const auto map = (type == QVariant::Map) ?
+ VariantOrderedMap(v.toMap()) : v.value<VariantOrderedMap>();
+
+ result = QLatin1String("Map {");
+ for (const auto &pair : map) {
+ result += indented + dumpVariant(pair.first, indented);
+ result.chop(1); // remove comma
+ result += QLatin1String(" => ") + dumpVariant(pair.second, indented);
+
+ }
+ result.chop(1); // remove comma
+ result += indent + QLatin1String("},");
+ } else if (type == QVariant::List) {
+ const QVariantList list = v.toList();
+
+ result = QLatin1String("List [");
+ for (const auto &item : list)
+ result += indented + dumpVariant(item, indented);
+ result.chop(1); // remove comma
+ result += indent + QLatin1String("],");
+ } else {
+ QDebug debug(&result);
+ debug.nospace() << v << ',';
+ }
+ return result;
+}
+
+QString DataStreamDumper::name()
+{
+ return QStringLiteral("datastream-dump");
+}
+
+Converter::Direction DataStreamDumper::directions()
+{
+ return Out;
+}
+
+Converter::Options DataStreamDumper::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *DataStreamDumper::optionsHelp()
+{
+ return nullptr;
+}
+
+bool DataStreamDumper::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNREACHABLE();
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ return QVariant();
+}
+
+void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ Q_UNUSED(options);
+ QString s = dumpVariant(contents);
+ s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline
+
+ QTextStream out(f);
+ out << s;
+}
+
+DataStreamConverter::DataStreamConverter()
+{
+ qRegisterMetaType<VariantOrderedMap>();
+ qRegisterMetaTypeStreamOperators<VariantOrderedMap>();
+}
+
+QString DataStreamConverter::name()
+{
+ return QStringLiteral("datastream");
+}
+
+Converter::Direction DataStreamConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options DataStreamConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *DataStreamConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool DataStreamConverter::probeFile(QIODevice *f)
+{
+ return f->isReadable() && f->peek(sizeof(signature) - 1) == signature;
+}
+
+QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = &dataStreamDumper;
+
+ char c;
+ if (f->read(sizeof(signature) -1) != signature ||
+ !f->getChar(&c) || (c != 'l' && c != 'B')) {
+ fprintf(stderr, "Could not load QDataStream file: invalid signature.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ QDataStream ds(f);
+ ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian);
+
+ std::underlying_type<QDataStream::Version>::type version;
+ ds >> version;
+ ds.setVersion(QDataStream::Version(version));
+
+ QVariant result;
+ ds >> result;
+ return result;
+}
+
+void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QDataStream::Version version = QDataStream::Qt_5_0;
+ auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
+ for (const QString &option : options) {
+ const QStringList pair = option.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "byteorder") {
+ if (pair.last() == "little") {
+ order = QDataStream::LittleEndian;
+ continue;
+ } else if (pair.last() == "big") {
+ order = QDataStream::BigEndian;
+ continue;
+ } else if (pair.last() == "host") {
+ order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
+ continue;
+ }
+ }
+ if (pair.first() == "version") {
+ bool ok;
+ int n = pair.last().toInt(&ok);
+ if (ok) {
+ version = QDataStream::Version(n);
+ continue;
+ }
+
+ fprintf(stderr, "Invalid version number '%s': must be a number from 1 to %d.\n",
+ qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s",
+ qPrintable(option), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ char c = order == QDataStream::LittleEndian ? 'l' : 'B';
+ f->write(signature);
+ f->write(&c, 1);
+
+ QDataStream ds(f);
+ ds.setVersion(version);
+ ds.setByteOrder(order);
+ ds << std::underlying_type<decltype(version)>::type(version);
+ ds << contents;
+}
diff --git a/examples/corelib/serialization/convert/datastreamconverter.h b/examples/corelib/serialization/convert/datastreamconverter.h
new file mode 100644
index 0000000000..1b74abc54f
--- /dev/null
+++ b/examples/corelib/serialization/convert/datastreamconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DATASTREAMCONVERTER_H
+#define DATASTREAMCONVERTER_H
+
+#include "converter.h"
+
+class DataStreamDumper : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class DataStreamConverter : public Converter
+{
+public:
+ DataStreamConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // DATASTREAMCONVERTER_H
diff --git a/examples/corelib/serialization/convert/jsonconverter.cpp b/examples/corelib/serialization/convert/jsonconverter.cpp
new file mode 100644
index 0000000000..80d1cc6827
--- /dev/null
+++ b/examples/corelib/serialization/convert/jsonconverter.cpp
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "jsonconverter.h"
+
+#include <QFile>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+
+static JsonConverter jsonConverter;
+static BinaryJsonConverter BinaryJsonConverter;
+
+static const char optionHelp[] =
+ "compact=no|yes Use compact JSON form.\n";
+
+static QJsonDocument convertFromVariant(const QVariant &v)
+{
+ QJsonDocument doc = QJsonDocument::fromVariant(v);
+ if (!doc.isObject() && !doc.isArray()) {
+ fprintf(stderr, "Could not convert contents to JSON.\n");
+ exit(EXIT_FAILURE);
+ }
+ return doc;
+}
+
+JsonConverter::JsonConverter()
+{
+}
+
+QString JsonConverter::name()
+{
+ return "json";
+}
+
+Converter::Direction JsonConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options JsonConverter::outputOptions()
+{
+ return {};
+}
+
+const char *JsonConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool JsonConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".json")))
+ return true;
+ }
+
+ if (f->isReadable()) {
+ QByteArray ba = f->peek(1);
+ return ba == "{" || ba == "[";
+ }
+ return false;
+}
+
+QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QJsonParseError error;
+ QJsonDocument doc;
+ if (auto file = qobject_cast<QFile *>(f)) {
+ const char *ptr = reinterpret_cast<char *>(file->map(0, file->size()));
+ if (ptr)
+ doc = QJsonDocument::fromJson(QByteArray::fromRawData(ptr, file->size()), &error);
+ }
+
+ if (doc.isNull())
+ doc = QJsonDocument::fromJson(f->readAll(), &error);
+ if (error.error) {
+ fprintf(stderr, "Could not parse JSON content: offset %d: %s",
+ error.offset, qPrintable(error.errorString()));
+ exit(EXIT_FAILURE);
+ }
+ if (outputConverter == null)
+ return QVariant();
+ return doc.toVariant();
+}
+
+void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QJsonDocument::JsonFormat format = QJsonDocument::Indented;
+ for (const QString &s : options) {
+ if (s == QLatin1String("compact=no")) {
+ format = QJsonDocument::Indented;
+ } else if (s == QLatin1String("compact=yes")) {
+ format = QJsonDocument::Compact;
+ } else {
+ fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s", qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ f->write(convertFromVariant(contents).toJson(format));
+}
+
+QString BinaryJsonConverter::name()
+{
+ return "binary-json";
+}
+
+Converter::Direction BinaryJsonConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options BinaryJsonConverter::outputOptions()
+{
+ return {};
+}
+
+const char *BinaryJsonConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool BinaryJsonConverter::probeFile(QIODevice *f)
+{
+ return f->isReadable() && f->peek(4) == "qbjs";
+}
+
+QVariant BinaryJsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = &jsonConverter;
+
+ QJsonDocument doc;
+ if (auto file = qobject_cast<QFile *>(f)) {
+ uchar *ptr = file->map(0, file->size());
+ if (ptr)
+ doc = QJsonDocument::fromRawData(reinterpret_cast<char *>(ptr), file->size());
+ }
+
+ if (doc.isNull())
+ doc = QJsonDocument::fromBinaryData(f->readAll());
+
+ if (!doc.isObject() && !doc.isArray()) {
+ fprintf(stderr, "Failed to load Binary JSON.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (outputConverter == null)
+ return QVariant();
+ return doc.toVariant();
+}
+
+void BinaryJsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to JSON output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ f->write(convertFromVariant(contents).toBinaryData());
+}
diff --git a/examples/corelib/serialization/convert/jsonconverter.h b/examples/corelib/serialization/convert/jsonconverter.h
new file mode 100644
index 0000000000..17170603c7
--- /dev/null
+++ b/examples/corelib/serialization/convert/jsonconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef JSONCONVERTER_H
+#define JSONCONVERTER_H
+
+#include "converter.h"
+
+class JsonConverter : public Converter
+{
+public:
+ JsonConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class BinaryJsonConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // JSONCONVERTER_H
diff --git a/examples/corelib/serialization/convert/main.cpp b/examples/corelib/serialization/convert/main.cpp
new file mode 100644
index 0000000000..e9d14792b0
--- /dev/null
+++ b/examples/corelib/serialization/convert/main.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "converter.h"
+
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QCoreApplication>
+#include <QFile>
+#include <QFileInfo>
+
+#include <stdio.h>
+
+static QVector<Converter *> *availableConverters;
+
+Converter::Converter()
+{
+ if (!availableConverters)
+ availableConverters = new QVector<Converter *>;
+ availableConverters->append(this);
+}
+
+Converter::~Converter()
+{
+ availableConverters->removeAll(this);
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QStringList inputFormats;
+ QStringList outputFormats;
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ auto direction = conv->directions();
+ QString name = conv->name();
+ if (direction & Converter::In)
+ inputFormats << name;
+ if (direction & Converter::Out)
+ outputFormats << name;
+ }
+ inputFormats.sort();
+ outputFormats.sort();
+ inputFormats.prepend("auto");
+ outputFormats.prepend("auto");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool"));
+ parser.addHelpOption();
+
+ QCommandLineOption inputFormatOption(QStringList{"I", "input-format"});
+ inputFormatOption.setDescription(QLatin1String("Select the input format for the input file. Available formats: ") +
+ inputFormats.join(", "));
+ inputFormatOption.setValueName("format");
+ inputFormatOption.setDefaultValue(inputFormats.constFirst());
+ parser.addOption(inputFormatOption);
+
+ QCommandLineOption outputFormatOption(QStringList{"O", "output-format"});
+ outputFormatOption.setDescription(QLatin1String("Select the output format for the output file. Available formats: ") +
+ outputFormats.join(", "));
+ outputFormatOption.setValueName("format");
+ outputFormatOption.setDefaultValue(outputFormats.constFirst());
+ parser.addOption(outputFormatOption);
+
+ QCommandLineOption optionOption(QStringList{"o", "option"});
+ optionOption.setDescription(QStringLiteral("Format-specific options. Use --format-options to find out what options are available."));
+ optionOption.setValueName("options...");
+ optionOption.setDefaultValues({});
+ parser.addOption(optionOption);
+
+ QCommandLineOption formatOptionsOption("format-options");
+ formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format <format>."));
+ formatOptionsOption.setValueName("format");
+ parser.addOption(formatOptionsOption);
+
+ parser.addPositionalArgument(QStringLiteral("[source]"),
+ QStringLiteral("File to read from (stdin if none)"));
+ parser.addPositionalArgument(QStringLiteral("[destination]"),
+ QStringLiteral("File to write to (stdout if none)"));
+
+ parser.process(app);
+
+ if (parser.isSet(formatOptionsOption)) {
+ QString format = parser.value(formatOptionsOption);
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ const char *help = conv->optionsHelp();
+ if (help)
+ printf("The following options are available for format '%s':\n\n%s", qPrintable(format), help);
+ else
+ printf("Format '%s' supports no options.\n", qPrintable(format));
+ return EXIT_SUCCESS;
+ }
+ }
+
+ fprintf(stderr, "Unknown file format '%s'\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+
+ Converter *inconv = nullptr;
+ QString format = parser.value(inputFormatOption);
+ if (format != "auto") {
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ inconv = conv;
+ break;
+ }
+ }
+
+ if (!inconv) {
+ fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+ }
+
+ Converter *outconv = nullptr;
+ format = parser.value(outputFormatOption);
+ if (format != "auto") {
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ outconv = conv;
+ break;
+ }
+ }
+
+ if (!outconv) {
+ fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+ }
+
+ QStringList files = parser.positionalArguments();
+ QFile input(files.value(0));
+ QFile output(files.value(1));
+
+ if (input.fileName().isEmpty())
+ input.open(stdin, QIODevice::ReadOnly);
+ else
+ input.open(QIODevice::ReadOnly);
+ if (!input.isOpen()) {
+ fprintf(stderr, "Could not open \"%s\" for reading: %s\n",
+ qPrintable(input.fileName()), qPrintable(input.errorString()));
+ return EXIT_FAILURE;
+ }
+
+ if (output.fileName().isEmpty())
+ output.open(stdout, QIODevice::WriteOnly | QIODevice::Truncate);
+ else
+ output.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ if (!output.isOpen()) {
+ fprintf(stderr, "Could not open \"%s\" for writing: %s\n",
+ qPrintable(output.fileName()), qPrintable(output.errorString()));
+ return EXIT_FAILURE;
+ }
+
+ if (!inconv) {
+ // probe the input to find a file format
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->directions() & Converter::In && conv->probeFile(&input)) {
+ inconv = conv;
+ break;
+ }
+ }
+
+ if (!inconv) {
+ fprintf(stderr, "Could not determine input format. pass -I option.\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!outconv) {
+ // probe the output to find a file format
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->directions() & Converter::Out && conv->probeFile(&output)) {
+ outconv = conv;
+ break;
+ }
+ }
+ }
+
+ // now finally perform the conversion
+ QVariant data = inconv->loadFile(&input, outconv);
+ Q_ASSERT_X(outconv, "Converter Tool",
+ "Internal error: converter format did not provide default");
+ outconv->saveFile(&output, data, parser.values(optionOption));
+ return EXIT_SUCCESS;
+}
diff --git a/examples/corelib/serialization/convert/nullconverter.cpp b/examples/corelib/serialization/convert/nullconverter.cpp
new file mode 100644
index 0000000000..2de492e64e
--- /dev/null
+++ b/examples/corelib/serialization/convert/nullconverter.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "nullconverter.h"
+
+static NullConverter nullConverter;
+Converter* Converter::null = &nullConverter;
+
+QString NullConverter::name()
+{
+ return QLatin1String("null");
+}
+
+Converter::Direction NullConverter::directions()
+{
+ return Out;
+}
+
+Converter::Options NullConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *NullConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool NullConverter::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ outputConverter = this;
+ return QVariant();
+}
+
+void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ Q_UNUSED(f);
+ Q_UNUSED(contents);
+}
diff --git a/examples/corelib/serialization/convert/nullconverter.h b/examples/corelib/serialization/convert/nullconverter.h
new file mode 100644
index 0000000000..af7339f092
--- /dev/null
+++ b/examples/corelib/serialization/convert/nullconverter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef NULLCONVERTER_H
+#define NULLCONVERTER_H
+
+#include "converter.h"
+
+class NullConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // NULLCONVERTER_H
diff --git a/examples/corelib/serialization/convert/textconverter.cpp b/examples/corelib/serialization/convert/textconverter.cpp
new file mode 100644
index 0000000000..e80e69a0b5
--- /dev/null
+++ b/examples/corelib/serialization/convert/textconverter.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "textconverter.h"
+
+#include <QFile>
+#include <QTextStream>
+
+static void dumpVariant(QTextStream &out, const QVariant &v)
+{
+ switch (v.userType()) {
+ case QVariant::List: {
+ const QVariantList list = v.toList();
+ for (const QVariant &item : list)
+ dumpVariant(out, item);
+ break;
+ }
+
+ case QVariant::String: {
+ const QStringList list = v.toStringList();
+ for (const QString &s : list)
+ out << s << endl;
+ break;
+ }
+
+ case QVariant::Map: {
+ const QVariantMap map = v.toMap();
+ for (auto it = map.begin(); it != map.end(); ++it) {
+ out << it.key() << " => ";
+ dumpVariant(out, it.value());
+ }
+ break;
+ }
+
+ case QMetaType::Nullptr:
+ out << "(null)" << endl;
+ break;
+
+ default:
+ out << v.toString() << endl;
+ break;
+ }
+}
+
+QString TextConverter::name()
+{
+ return QStringLiteral("text");
+}
+
+Converter::Direction TextConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options TextConverter::outputOptions()
+{
+ return {};
+}
+
+const char *TextConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool TextConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f))
+ return file->fileName().endsWith(QLatin1String(".txt"));
+ return false;
+}
+
+QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QVariantList list;
+ QTextStream in(f);
+ QString line ;
+ while (!in.atEnd()) {
+ in.readLineInto(&line);
+
+ bool ok;
+ qint64 v = line.toLongLong(&ok);
+ if (ok) {
+ list.append(v);
+ continue;
+ }
+
+ double d = line.toDouble(&ok);
+ if (ok) {
+ list.append(d);
+ continue;
+ }
+
+ list.append(line);
+ }
+
+ return list;
+}
+
+void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ QTextStream out(f);
+ dumpVariant(out, contents);
+}
+
+static TextConverter textConverter;
diff --git a/examples/corelib/serialization/convert/textconverter.h b/examples/corelib/serialization/convert/textconverter.h
new file mode 100644
index 0000000000..66f5136c02
--- /dev/null
+++ b/examples/corelib/serialization/convert/textconverter.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEXTCONVERTER_H
+#define TEXTCONVERTER_H
+
+#include "converter.h"
+
+class TextConverter : public Converter
+{
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // TEXTCONVERTER_H
diff --git a/examples/corelib/serialization/convert/xmlconverter.cpp b/examples/corelib/serialization/convert/xmlconverter.cpp
new file mode 100644
index 0000000000..62908273ce
--- /dev/null
+++ b/examples/corelib/serialization/convert/xmlconverter.cpp
@@ -0,0 +1,514 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "xmlconverter.h"
+
+#include <QBitArray>
+#include <QtCborCommon>
+#include <QFile>
+#include <QFloat16>
+#include <QMetaType>
+#include <QRegularExpression>
+#include <QUrl>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+static const char optionHelp[] =
+ "compact=no|yes Use compact XML form.\n";
+
+static XmlConverter xmlConverter;
+
+static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options);
+
+static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariantList list;
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ list << variantFromXml(xml, options);
+ continue;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ return list;
+}
+
+static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariant key, value;
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (value.isValid())
+ break;
+ if (key.isValid())
+ value = variantFromXml(xml, options);
+ else
+ key = variantFromXml(xml, options);
+ continue;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ return { key, value };
+}
+
+static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariantMap map1;
+ VariantOrderedMap map2;
+
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (xml.name() == QLatin1String("entry")) {
+ auto pair = mapEntryFromXml(xml, options);
+ if (options & Converter::SupportsArbitraryMapKeys)
+ map2.append(pair);
+ else
+ map1.insert(pair.first.toString(), pair.second);
+ continue;
+ }
+ break;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ if (options & Converter::SupportsArbitraryMapKeys)
+ return QVariant::fromValue(map2);
+ return map1;
+}
+
+static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QStringRef name = xml.name();
+ if (name == QLatin1String("list"))
+ return listFromXml(xml, options);
+ if (name == QLatin1String("map"))
+ return mapFromXml(xml, options);
+ if (name != QLatin1String("value")) {
+ fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ QXmlStreamAttributes attrs = xml.attributes();
+ QStringRef type = attrs.value(QLatin1String("type"));
+
+ forever {
+ xml.readNext();
+ if (xml.isComment())
+ continue;
+ if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
+ break;
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ QStringRef text = xml.text();
+ if (!xml.isCDATA())
+ text = text.trimmed();
+
+ QVariant result;
+ bool ok;
+ if (type.isEmpty()) {
+ // ok
+ } else if (type == QLatin1String("number")) {
+ // try integer first
+ qint64 v = text.toLongLong(&ok);
+ if (ok) {
+ result = v;
+ } else {
+ // let's see floating point
+ double d = text.toDouble(&ok);
+ result = d;
+ if (!ok) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else if (type == QLatin1String("bytes")) {
+ QByteArray data = text.toLatin1();
+ QStringRef encoding = attrs.value("encoding");
+ if (encoding == QLatin1String("base64url")) {
+ result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding);
+ } else if (encoding == QLatin1String("hex")) {
+ result = QByteArray::fromHex(data);
+ } else if (encoding.isEmpty() || encoding == QLatin1String("base64")) {
+ result = QByteArray::fromBase64(data);
+ } else {
+ fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
+ exit(EXIT_FAILURE);
+ }
+ } else if (type == QLatin1String("string")) {
+ result = text.toString();
+ } else if (type == QLatin1String("null")) {
+ result = QVariant::fromValue(nullptr);
+ } else if (type == QLatin1String("CBOR simple type")) {
+ result = QVariant::fromValue(QCborSimpleType(text.toShort()));
+ } else if (type == QLatin1String("bits")) {
+ QBitArray ba;
+ ba.resize(text.size());
+ qsizetype n = 0;
+ for (qsizetype i = 0; i < text.size(); ++i) {
+ QChar c = text.at(i);
+ if (c == '1') {
+ ba.setBit(n++);
+ } else if (c == '0') {
+ ++n;
+ } else if (!c.isSpace()) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: invalid bit string '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+ ba.resize(n);
+ result = ba;
+ } else {
+ int id = QVariant::Invalid;
+ if (type == QLatin1String("datetime"))
+ id = QVariant::DateTime;
+ else if (type == QLatin1String("url"))
+ id = QVariant::Url;
+ else if (type == QLatin1String("uuid"))
+ id = QVariant::Uuid;
+ else if (type == QLatin1String("regex"))
+ id = QVariant::RegularExpression;
+ else
+ id = QMetaType::type(type.toLatin1());
+ if (id == QVariant::Invalid) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ result = text.toString();
+ if (!result.convert(id)) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: could not parse content as type '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ do {
+ xml.readNext();
+ } while (xml.isComment() || xml.isWhitespace());
+
+ if (!xml.isEndElement()) {
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ return result;
+}
+
+static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
+{
+ int type = v.userType();
+ if (type == QVariant::List) {
+ QVariantList list = v.toList();
+ xml.writeStartElement("list");
+ for (const QVariant &v : list)
+ variantToXml(xml, v);
+ xml.writeEndElement();
+ } else if (type == QVariant::Map || type == qMetaTypeId<VariantOrderedMap>()) {
+ const VariantOrderedMap map = (type == QVariant::Map) ?
+ VariantOrderedMap(v.toMap()) :
+ v.value<VariantOrderedMap>();
+
+ xml.writeStartElement("map");
+ for (const auto &pair : map) {
+ xml.writeStartElement("entry");
+ variantToXml(xml, pair.first);
+ variantToXml(xml, pair.second);
+ xml.writeEndElement();
+ }
+ xml.writeEndElement();
+ } else {
+ xml.writeStartElement("value");
+ QString typeString = QStringLiteral("type");
+ switch (type) {
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::Float:
+ case QMetaType::Double:
+ xml.writeAttribute(typeString, "number");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QByteArray:
+ xml.writeAttribute(typeString, "bytes");
+ xml.writeAttribute("encoding", "base64");
+ xml.writeCharacters(QString::fromLatin1(v.toByteArray().toBase64()));
+ break;
+
+ case QMetaType::QString:
+ xml.writeAttribute(typeString, "string");
+ xml.writeCDATA(v.toString());
+ break;
+
+ case QMetaType::Bool:
+ xml.writeAttribute(typeString, "bool");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::Nullptr:
+ xml.writeAttribute(typeString, "null");
+ break;
+
+ case QMetaType::UnknownType:
+ break;
+
+ case QMetaType::QDate:
+ case QMetaType::QTime:
+ case QMetaType::QDateTime:
+ xml.writeAttribute(typeString, "dateime");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QUrl:
+ xml.writeAttribute(typeString, "url");
+ xml.writeCharacters(v.toUrl().toString(QUrl::FullyEncoded));
+ break;
+
+ case QMetaType::QUuid:
+ xml.writeAttribute(typeString, "uuid");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QBitArray:
+ xml.writeAttribute(typeString, "bits");
+ xml.writeCharacters([](const QBitArray &ba) {
+ QString result;
+ for (qsizetype i = 0; i < ba.size(); ++i) {
+ if (i && i % 72 == 0)
+ result += '\n';
+ result += QLatin1Char(ba.testBit(i) ? '1' : '0');
+ }
+ return result;
+ }(v.toBitArray()));
+ break;
+
+ case QMetaType::QRegularExpression:
+ xml.writeAttribute(typeString, "regex");
+ xml.writeCharacters(v.toRegularExpression().pattern());
+ break;
+
+ default:
+ if (type == qMetaTypeId<qfloat16>()) {
+ xml.writeAttribute(typeString, "number");
+ xml.writeCharacters(QString::number(float(v.value<qfloat16>())));
+ } else if (type == qMetaTypeId<QCborSimpleType>()) {
+ xml.writeAttribute(typeString, "CBOR simple type");
+ xml.writeCharacters(QString::number(int(v.value<QCborSimpleType>())));
+ } else {
+ // does this convert to string?
+ const char *typeName = v.typeName();
+ QVariant copy = v;
+ if (copy.convert(QVariant::String)) {
+ xml.writeAttribute(typeString, QString::fromLatin1(typeName));
+ xml.writeCharacters(copy.toString());
+ } else {
+ fprintf(stderr, "XML: don't know how to serialize type '%s'.\n", typeName);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ xml.writeEndElement();
+ }
+}
+
+QString XmlConverter::name()
+{
+ return QStringLiteral("xml");
+}
+
+Converter::Direction XmlConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options XmlConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *XmlConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool XmlConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".xml")))
+ return true;
+ }
+
+ return f->isReadable() && f->peek(5) == "<?xml";
+}
+
+QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QXmlStreamReader xml(f);
+ xml.readNextStartElement();
+ QVariant v = variantFromXml(xml, outputConverter->outputOptions());
+ if (xml.hasError()) {
+ fprintf(stderr, "XML error: %s", qPrintable(xml.errorString()));
+ exit(EXIT_FAILURE);
+ }
+
+ return v;
+}
+
+void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ bool compact = false;
+ for (const QString &s : options) {
+ if (s == QLatin1String("compact=no")) {
+ compact = false;
+ } else if (s == QLatin1String("compact=yes")) {
+ compact = true;
+ } else {
+ fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s", qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ QXmlStreamWriter xml(f);
+ xml.setAutoFormatting(!compact);
+ xml.writeStartDocument();
+ variantToXml(xml, contents);
+ xml.writeEndDocument();
+}
diff --git a/examples/corelib/serialization/convert/xmlconverter.h b/examples/corelib/serialization/convert/xmlconverter.h
new file mode 100644
index 0000000000..8fc0fea592
--- /dev/null
+++ b/examples/corelib/serialization/convert/xmlconverter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef XMLCONVERTER_H
+#define XMLCONVERTER_H
+
+#include "converter.h"
+
+class XmlConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // XMLCONVERTER_H
diff --git a/examples/corelib/serialization/serialization.pro b/examples/corelib/serialization/serialization.pro
index af4d3e6f0f..7651444f19 100644
--- a/examples/corelib/serialization/serialization.pro
+++ b/examples/corelib/serialization/serialization.pro
@@ -1,2 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS = savegame
+SUBDIRS = \
+ cbordump \
+ convert \
+ savegame