mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-05 16:55:25 +08:00
6.6.1 original
This commit is contained in:
@ -20,13 +20,14 @@ qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(convert
|
||||
cborconverter.cpp cborconverter.h
|
||||
converter.h
|
||||
converter.cpp converter.h
|
||||
datastreamconverter.cpp datastreamconverter.h
|
||||
debugtextdumper.cpp debugtextdumper.h
|
||||
jsonconverter.cpp jsonconverter.h
|
||||
main.cpp
|
||||
nullconverter.cpp nullconverter.h
|
||||
textconverter.cpp textconverter.h
|
||||
variantorderedmap.h
|
||||
xmlconverter.cpp xmlconverter.h
|
||||
)
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "cborconverter.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QCborArray>
|
||||
#include <QCborMap>
|
||||
@ -9,6 +10,7 @@
|
||||
#include <QCborStreamWriter>
|
||||
#include <QCborValue>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFloat16>
|
||||
#include <QMetaType>
|
||||
@ -57,9 +59,9 @@ QT_END_NAMESPACE
|
||||
// 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.
|
||||
|
||||
//! [0]
|
||||
static QVariant convertCborValue(const QCborValue &value);
|
||||
|
||||
//! [0]
|
||||
static QVariant convertCborMap(const QCborMap &map)
|
||||
{
|
||||
VariantOrderedMap result;
|
||||
@ -87,8 +89,9 @@ static QVariant convertCborValue(const QCborValue &value)
|
||||
return value.toVariant();
|
||||
}
|
||||
//! [0]
|
||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||
|
||||
//! [1]
|
||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
|
||||
{
|
||||
if (v.userType() == QMetaType::QVariantList) {
|
||||
@ -140,20 +143,6 @@ const char *CborDiagnosticDumper::optionsHelp() const
|
||||
return diagnosticHelp;
|
||||
}
|
||||
|
||||
bool CborDiagnosticDumper::probeFile(QIODevice *f) const
|
||||
{
|
||||
Q_UNUSED(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||
{
|
||||
Q_UNREACHABLE();
|
||||
Q_UNUSED(f);
|
||||
Q_UNUSED(outputConverter);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const
|
||||
{
|
||||
@ -178,9 +167,8 @@ void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
|
||||
qPrintable(s), diagnosticHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
|
||||
qPrintable(s), diagnosticHelp);
|
||||
}
|
||||
|
||||
QTextStream out(f);
|
||||
@ -221,7 +209,6 @@ bool CborConverter::probeFile(QIODevice *f) const
|
||||
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
|
||||
}
|
||||
|
||||
//! [2]
|
||||
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||
{
|
||||
const char *ptr = nullptr;
|
||||
@ -239,28 +226,25 @@ QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter
|
||||
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);
|
||||
qFatal().nospace()
|
||||
<< "Error loading CBOR contents (byte " << offset
|
||||
<< "): " << reader.lastError().toString()
|
||||
<< "\n bytes: " << (ptr ? mapped.mid(offset, 9) : f->read(9));
|
||||
} else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) {
|
||||
fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
|
||||
qWarning("Warning: bytes remaining at the end of the CBOR stream");
|
||||
}
|
||||
|
||||
if (outputConverter == nullptr)
|
||||
outputConverter = &cborDiagnosticDumper;
|
||||
else if (outputConverter == null)
|
||||
else if (isNull(outputConverter))
|
||||
return QVariant();
|
||||
else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
|
||||
return contents.toVariant();
|
||||
return convertCborValue(contents);
|
||||
}
|
||||
//! [2]
|
||||
//! [3]
|
||||
|
||||
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
|
||||
{
|
||||
//! [3]
|
||||
bool useSignature = true;
|
||||
bool useIntegers = true;
|
||||
enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
|
||||
@ -315,11 +299,10 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s",
|
||||
qPrintable(s), cborOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown CBOR format option '%s'. Valid options are:\n%s",
|
||||
qPrintable(s), cborOptionHelp);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
QCborValue v =
|
||||
convertFromVariant(contents,
|
||||
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
|
||||
@ -336,4 +319,3 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
|
||||
opts |= QCborValue::UseFloat16;
|
||||
v.toCbor(writer, opts);
|
||||
}
|
||||
//! [4]
|
||||
|
@ -14,8 +14,6 @@ public:
|
||||
Directions directions() const override;
|
||||
Options outputOptions() const override;
|
||||
const char *optionsHelp() const override;
|
||||
bool probeFile(QIODevice *f) const override;
|
||||
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
|
||||
void saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert
|
||||
INSTALLS += target
|
||||
|
||||
SOURCES += main.cpp \
|
||||
converter.cpp \
|
||||
cborconverter.cpp \
|
||||
datastreamconverter.cpp \
|
||||
debugtextdumper.cpp \
|
||||
@ -27,4 +28,5 @@ HEADERS += \
|
||||
jsonconverter.h \
|
||||
nullconverter.h \
|
||||
textconverter.h \
|
||||
variantorderedmap.h \
|
||||
xmlconverter.h
|
||||
|
44
examples/corelib/serialization/convert/converter.cpp
Normal file
44
examples/corelib/serialization/convert/converter.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "converter.h"
|
||||
|
||||
//! [0]
|
||||
Converter::Converter()
|
||||
{
|
||||
converters().append(this);
|
||||
}
|
||||
|
||||
Converter::~Converter()
|
||||
{
|
||||
converters().removeAll(this);
|
||||
}
|
||||
|
||||
QList<const Converter *> &Converter::converters()
|
||||
{
|
||||
Q_CONSTINIT static QList<const Converter *> store;
|
||||
return store;
|
||||
}
|
||||
|
||||
const QList<const Converter *> &Converter::allConverters()
|
||||
{
|
||||
return converters();
|
||||
}
|
||||
//! [0]
|
||||
|
||||
// Some virtual methods that Converter classes needn't override, when not relevant:
|
||||
Converter::Options Converter::outputOptions() const { return {}; }
|
||||
const char *Converter::optionsHelp() const { return nullptr; }
|
||||
bool Converter::probeFile(QIODevice *) const { return false; }
|
||||
|
||||
// The virtual method they should override if they claim to support In:
|
||||
QVariant Converter::loadFile(QIODevice *, const Converter *&outputConverter) const
|
||||
{
|
||||
Q_ASSERT(!directions().testFlag(Converter::Direction::In));
|
||||
// For those that don't, this should never be called.
|
||||
Q_UNIMPLEMENTED();
|
||||
// But every implementation should at least do this:
|
||||
if (!outputConverter)
|
||||
outputConverter = this;
|
||||
return QVariant();
|
||||
}
|
@ -6,31 +6,19 @@
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
||||
class VariantOrderedMap : public QList<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)
|
||||
|
||||
//! [0]
|
||||
class Converter
|
||||
{
|
||||
static QList<const Converter *> &converters();
|
||||
protected:
|
||||
Converter();
|
||||
static bool isNull(const Converter *converter); // in nullconverter.cpp
|
||||
|
||||
public:
|
||||
static Converter *null;
|
||||
static const QList<const Converter *> &allConverters();
|
||||
|
||||
enum class Direction { In = 1, Out = 2, InOut = In | Out };
|
||||
Q_DECLARE_FLAGS(Directions, Direction)
|
||||
@ -42,15 +30,16 @@ public:
|
||||
|
||||
virtual QString name() const = 0;
|
||||
virtual Directions directions() const = 0;
|
||||
virtual Options outputOptions() const = 0;
|
||||
virtual const char *optionsHelp() const = 0;
|
||||
virtual bool probeFile(QIODevice *f) const = 0;
|
||||
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const = 0;
|
||||
virtual Options outputOptions() const;
|
||||
virtual const char *optionsHelp() const;
|
||||
virtual bool probeFile(QIODevice *f) const;
|
||||
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const;
|
||||
virtual void saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
|
||||
//! [0]
|
||||
|
||||
#endif // CONVERTER_H
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "datastreamconverter.h"
|
||||
#include "debugtextdumper.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
@ -79,10 +80,8 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputCon
|
||||
outputConverter = &debugTextDumper;
|
||||
|
||||
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);
|
||||
}
|
||||
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B'))
|
||||
qFatal("Could not load QDataStream file: invalid signature.");
|
||||
|
||||
QDataStream ds(f);
|
||||
ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
@ -124,15 +123,13 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
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);
|
||||
qFatal("Invalid version number '%s': must be a number from 1 to %d.",
|
||||
qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s",
|
||||
qFatal("Unknown QDataStream formatting option '%s'. Available options are:\n%s",
|
||||
qPrintable(option), dataStreamOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "debugtextdumper.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
@ -58,29 +59,13 @@ Converter::Options DebugTextDumper::outputOptions() const
|
||||
return SupportsArbitraryMapKeys;
|
||||
}
|
||||
|
||||
const char *DebugTextDumper::optionsHelp() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DebugTextDumper::probeFile(QIODevice *f) const
|
||||
{
|
||||
Q_UNUSED(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant DebugTextDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||
{
|
||||
Q_UNREACHABLE();
|
||||
Q_UNUSED(f);
|
||||
Q_UNUSED(outputConverter);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const
|
||||
{
|
||||
Q_UNUSED(options);
|
||||
if (!options.isEmpty()) {
|
||||
qFatal("Unknown option '%s' to debug text output. This format has no options.",
|
||||
qPrintable(options.first()));
|
||||
}
|
||||
QString s = dumpVariant(contents);
|
||||
s[s.size() - 1] = u'\n'; // replace the comma with newline
|
||||
|
||||
|
@ -13,9 +13,6 @@ public:
|
||||
QString name() const override;
|
||||
Directions directions() const override;
|
||||
Options outputOptions() const override;
|
||||
const char *optionsHelp() const override;
|
||||
bool probeFile(QIODevice *f) const override;
|
||||
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
|
||||
void saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 7.5 KiB |
@ -5,78 +5,152 @@
|
||||
\example serialization/convert
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\meta tag {network}
|
||||
\title Convert Example
|
||||
\title Serialization Converter
|
||||
|
||||
\brief The Convert example demonstrates how to convert between different
|
||||
serialization formats.
|
||||
\brief How to convert between different serialization formats.
|
||||
|
||||
The Convert example converts between the serialization formats JSON, CBOR,
|
||||
XML, QDataStream and text. It can also auto detect the format being used.
|
||||
Not all formats support both input and output, and they have different
|
||||
sets of which types they support. QDataStream and XML are the richest,
|
||||
followed by CBOR, then JSON, and then the plain text one.
|
||||
This example converts between JSON, CBOR, XML, QDataStream and some simple
|
||||
text formats. It can auto-detect the format being used, or be told which
|
||||
format to use. Not all formats support both input and output, and they have
|
||||
different sets of which content datatypes they support. QDataStream and XML
|
||||
are the richest, followed by CBOR, then JSON, and then the plain text
|
||||
formats. Conversion via the less capable formats is apt to lose structure
|
||||
from the data.
|
||||
|
||||
\image convert.png
|
||||
|
||||
\sa {Parsing and displaying CBOR data}, {JSON Save Game}
|
||||
|
||||
\section1 The Converter Class
|
||||
|
||||
The Converter class is the abstract superclass for all the converters to
|
||||
and from all the formats. They all convert to and from the QVariant class,
|
||||
which is used to represent all the datastructures internally.
|
||||
The Converter class is the abstract superclass for all the converters to and
|
||||
from all the formats. They all convert from or to the QVariant class, which
|
||||
is used to represent all the datastructures internally.
|
||||
|
||||
\snippet serialization/convert/converter.h 0
|
||||
|
||||
The Converter constructor and destructor manage a list of available
|
||||
converters used by the main program so that it knows what converters are
|
||||
available. Each converter type defines a static instance that ensures it is
|
||||
constructed and thus available to the main program via this list. The \c
|
||||
allConverters() method provides \c main()'s code with access to the list.
|
||||
|
||||
\snippet serialization/convert/converter.cpp 0
|
||||
|
||||
The name() function returns the name of the converter. The directions()
|
||||
function is used to determine if a converter can be used for input, output,
|
||||
or both. The outputOptions() and optionsHelp() functions are used to get
|
||||
and query which options are used by the different converters. The
|
||||
probeFile() function is used to determine if a file has the same file
|
||||
format as the converter. The loadFile() function deserializes the given
|
||||
file, while the saveFile() serializes to the given file.
|
||||
or both. These enable the main program to report what converters are
|
||||
available in its help text for the command-line options to select input and
|
||||
output formats.
|
||||
|
||||
\section1 The CborConverter Class
|
||||
\snippet serialization/convert/main.cpp 0
|
||||
|
||||
The optionsHelp() function is used to report the various command-line
|
||||
options supported by the available formats, when queried using its \c
|
||||
{--format-options <format>} command-line option.
|
||||
|
||||
\snippet serialization/convert/main.cpp 1
|
||||
|
||||
The outputOptions() function reports the output capabilities of a converter.
|
||||
At present the only optional feature is support for arbitrary keys in
|
||||
mappings from keys to values. An input converter's loadFile() can use this
|
||||
information to tailor the form in which it presents the data it has read, to
|
||||
be as faithfully represented by the output converter as its capabilities
|
||||
permit.
|
||||
|
||||
The probeFile() function is used to determine if a file matches the format
|
||||
of the converter. The main program uses this to determine what format to use
|
||||
when reading or writing a file, based on its name and potentially content,
|
||||
when the user has not specified the format to use on the command-line.
|
||||
|
||||
The loadFile() function deserializes data. The caller tells loadFile() which
|
||||
serializer it intends to use, so that loadFile() can query its
|
||||
outputOptions() to determine the form in which to represent the loaded data.
|
||||
If the caller hasn't settled on a choice of output converter, loadFile()
|
||||
supplies it with a default output converter suitable to the data it is
|
||||
returning.
|
||||
|
||||
The saveFile() function serializes data. It is passed options from the
|
||||
command-line, as described by loadHelp(), that can tune the details of how
|
||||
it represents the data when saving to file.
|
||||
|
||||
Both loadFile() and saveFile() can be used with an arbitrary \l QIODevice.
|
||||
This means that a Converter could also be used with a network socket or
|
||||
other source of data, to read from or write to. In the present program, the
|
||||
main program always passes a \l QFile, accessing either a file on disk or
|
||||
one of the standard streams of the process.
|
||||
|
||||
\section2 The Available Converters
|
||||
|
||||
Several converters are supported, illustrating how the converter program
|
||||
could be adapted to other formats, should the need arise. See the source
|
||||
code for each for its details. The CBOR converters serve as a relatively
|
||||
full-featured illustration of the ways converters can work, that we'll look
|
||||
into in more detail below. This table summarizes the available converters:
|
||||
|
||||
\table
|
||||
\header \li Class \li mode \li format
|
||||
\row \li CborConverter \li In/Out \li CBOR
|
||||
\row \li CborDiagnosticDumper \li Out \li CBOR diagnostic
|
||||
\row \li DataStreamConverter \li In/Out \li QDataStream
|
||||
\row \li DebugTextDumper \li Out \li Lossless, non-standard, human-readable
|
||||
\row \li JsonConverter \li In/Out \li JSON
|
||||
\row \li NullConverter \li Out \li No output
|
||||
\row \li TextConverter \li In/Out \li Structured plain text
|
||||
\row \li XmlConverter \li In/Out \li XML
|
||||
\endtable
|
||||
|
||||
Those that support input use themselves as loadFile()'s fallback converter,
|
||||
except for the CBOR and QDataStream converters, which use their respective
|
||||
output-only dumper companion classes. The null converter can be used as
|
||||
output converter when running the program for the sake of any validation or
|
||||
verification that an input converter may perform.
|
||||
|
||||
\section2 The CborConverter and CborDiagnosticDumper Classes
|
||||
|
||||
The CborConverter class supports serializing to and from the CBOR format.
|
||||
It supports various options to configure the output of floating point values
|
||||
and a \c{signature} option to determine whether to start its output with a
|
||||
CBOR tag that serves as a file header, identifying the file as containing
|
||||
CBOR data.
|
||||
|
||||
The CborConverter class shows how to serialize to and from the CBOR-format.
|
||||
There is also a CborDiagnosticDumper class to output in CBOR diagnostic
|
||||
notation. That is similar to JSON, but not exactly, because it allows
|
||||
displaying the contents of a CBOR stream losslessly, while a conversion
|
||||
to JSON is lossy.
|
||||
notation. It does not support loading data. The form of its output can be
|
||||
configured using two options. One selects whether to use the (more verbose)
|
||||
extended CBOR diagnostic format. The other control whether each CBOR value
|
||||
appears on a separate line.
|
||||
|
||||
The plain diagnostic notation is similar to JSON, but not exactly, because
|
||||
it supports displaying the contents of a CBOR stream losslessly, while a
|
||||
conversion to JSON can be lossy. CborConverter's loadFile() uses
|
||||
CborDiagnosticDumper for the fallback output converter, if its caller hasn't
|
||||
determined the output format for itself.
|
||||
|
||||
The convertCborValue(), convertCborMap() and convertCborArray() helper
|
||||
functions are used to convert a QCborValue to a QVariant, for the benefit of
|
||||
CborConverter::loadFile().
|
||||
|
||||
The convertCborValue() function is used to convert a QCborValue to a
|
||||
QVariant. It uses the helper functions convertCborMap() and
|
||||
convertCborArray().
|
||||
\snippet serialization/convert/cborconverter.cpp 0
|
||||
|
||||
A CBOR-file is read using loadFile() function.
|
||||
\snippet serialization/convert/cborconverter.cpp 2
|
||||
|
||||
The convertFromVariant() function is used to convert a QVariant to a
|
||||
QCborValue.
|
||||
\snippet serialization/convert/cborconverter.cpp 1
|
||||
QCborValue for output by the \c saveFile() of either class.
|
||||
|
||||
A CBOR-file is written using the saveFile() function.
|
||||
\snippet serialization/convert/cborconverter.cpp 3
|
||||
\snippet serialization/convert/cborconverter.cpp 4
|
||||
\snippet serialization/convert/cborconverter.cpp 1
|
||||
|
||||
\sa {CBOR Support in Qt}
|
||||
|
||||
\section1 The DataStreamConverter Class
|
||||
\section1 The convert program
|
||||
|
||||
The DataStreamConverter class is used to serialize to and from the
|
||||
QDataStream format. There is also the DebugTextDumper class for outputting
|
||||
the data lossless in a non-standardized human readable format.
|
||||
The \c main() function sets up a \l QApplication and a \l QCommandLineParser
|
||||
to make sense of the options the user has specified and provide help if the
|
||||
user asks for it. It uses the values obtained for the various \l
|
||||
QCommandLineOption instances describing the user's choices, plus the
|
||||
positional arguments for file names, to prepare the converters it will use.
|
||||
|
||||
\section1 The JsonConverter Class
|
||||
It then uses its input converter to load data (and possibly resolve its
|
||||
choice of output converter, if it hasn't selected one yet) and its output
|
||||
converter to serialize that data, taking account of any output options the
|
||||
user has supplied on the command-line.
|
||||
|
||||
The JsonConverter class is used to serialize to and from the JSON-format.
|
||||
\sa {JSON Support in Qt}
|
||||
|
||||
\section1 The XmlConverter Class
|
||||
|
||||
The XmlConverter class is used to serialize to and from the XML-format.
|
||||
|
||||
\section1 The TextConverter Class
|
||||
|
||||
The TextConverter class is used to serialize to and from a text format.
|
||||
|
||||
\section1 The NullConverter Class
|
||||
|
||||
The NullConverter class is an output serializer that does nothing.
|
||||
\snippet serialization/convert/main.cpp 2
|
||||
*/
|
||||
|
@ -18,10 +18,8 @@ static const char jsonOptionHelp[] = "compact=no|yes Use compact JS
|
||||
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);
|
||||
}
|
||||
if (!doc.isObject() && !doc.isArray())
|
||||
qFatal("Could not convert contents to JSON.");
|
||||
return doc;
|
||||
}
|
||||
|
||||
@ -35,11 +33,6 @@ Converter::Directions JsonConverter::directions() const
|
||||
return Direction::InOut;
|
||||
}
|
||||
|
||||
Converter::Options JsonConverter::outputOptions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const char *JsonConverter::optionsHelp() const
|
||||
{
|
||||
return jsonOptionHelp;
|
||||
@ -75,11 +68,10 @@ QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter
|
||||
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);
|
||||
qFatal("Could not parse JSON content: offset %d: %s",
|
||||
error.offset, qPrintable(error.errorString()));
|
||||
}
|
||||
if (outputConverter == null)
|
||||
if (isNull(outputConverter))
|
||||
return QVariant();
|
||||
return doc.toVariant();
|
||||
}
|
||||
@ -94,9 +86,8 @@ void JsonConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
} else if (s == "compact=yes"_L1) {
|
||||
format = QJsonDocument::Compact;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s",
|
||||
qPrintable(s), jsonOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown option '%s' to JSON output. Valid options are:\n%s",
|
||||
qPrintable(s), jsonOptionHelp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ class JsonConverter : public Converter
|
||||
public:
|
||||
QString name() const override;
|
||||
Directions directions() const override;
|
||||
Options outputOptions() const override;
|
||||
const char *optionsHelp() const override;
|
||||
bool probeFile(QIODevice *f) const override;
|
||||
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "converter.h"
|
||||
@ -13,27 +14,57 @@
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
static QList<const Converter *> *availableConverters;
|
||||
|
||||
Converter::Converter()
|
||||
static const Converter *prepareConverter(QString format, Converter::Direction direction,
|
||||
QFile *stream)
|
||||
{
|
||||
if (!availableConverters)
|
||||
availableConverters = new QList<const Converter *>;
|
||||
availableConverters->append(this);
|
||||
}
|
||||
const bool out = direction == Converter::Direction::Out;
|
||||
const QIODevice::OpenMode mode = out
|
||||
? QIODevice::WriteOnly | QIODevice::Truncate
|
||||
: QIODevice::ReadOnly;
|
||||
const char *dirn = out ? "output" : "input";
|
||||
|
||||
Converter::~Converter()
|
||||
{
|
||||
availableConverters->removeAll(this);
|
||||
if (stream->fileName().isEmpty())
|
||||
stream->open(out ? stdout : stdin, mode);
|
||||
else
|
||||
stream->open(mode);
|
||||
|
||||
if (!stream->isOpen()) {
|
||||
qFatal("Could not open \"%s\" for %s: %s",
|
||||
qPrintable(stream->fileName()), dirn, qPrintable(stream->errorString()));
|
||||
} else if (format == "auto"_L1) {
|
||||
for (const Converter *conv : Converter::allConverters()) {
|
||||
if (conv->directions().testFlag(direction) && conv->probeFile(stream))
|
||||
return conv;
|
||||
}
|
||||
if (out) // Failure to identify output format can be remedied by loadFile().
|
||||
return nullptr;
|
||||
|
||||
// Input format, however, we must know before we can call that:
|
||||
qFatal("Could not determine input format. Specify it with the -I option.");
|
||||
} else {
|
||||
for (const Converter *conv : Converter::allConverters()) {
|
||||
if (conv->name() == format) {
|
||||
if (!conv->directions().testFlag(direction)) {
|
||||
qWarning("File format \"%s\" cannot be used for %s",
|
||||
qPrintable(format), dirn);
|
||||
continue; // on the off chance there's another with the same name
|
||||
}
|
||||
return conv;
|
||||
}
|
||||
}
|
||||
qFatal("Unknown %s file format \"%s\"", dirn, qPrintable(format));
|
||||
}
|
||||
Q_UNREACHABLE_RETURN(nullptr);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
//! [0]
|
||||
QStringList inputFormats;
|
||||
QStringList outputFormats;
|
||||
for (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
for (const Converter *conv : Converter::allConverters()) {
|
||||
auto direction = conv->directions();
|
||||
QString name = conv->name();
|
||||
if (direction.testFlag(Converter::Direction::In))
|
||||
@ -41,13 +72,14 @@ int main(int argc, char *argv[])
|
||||
if (direction.testFlag(Converter::Direction::Out))
|
||||
outputFormats << name;
|
||||
}
|
||||
//! [0]
|
||||
inputFormats.sort();
|
||||
outputFormats.sort();
|
||||
inputFormats.prepend("auto"_L1);
|
||||
outputFormats.prepend("auto"_L1);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("Qt file format conversion tool"_L1);
|
||||
parser.setApplicationDescription("Qt serialization format conversion tool"_L1);
|
||||
parser.addHelpOption();
|
||||
|
||||
QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 });
|
||||
@ -86,110 +118,38 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (parser.isSet(formatOptionsOption)) {
|
||||
QString format = parser.value(formatOptionsOption);
|
||||
for (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
//! [1]
|
||||
for (const Converter *conv : Converter::allConverters()) {
|
||||
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);
|
||||
qInfo("The following options are available for format '%s':\n\n%s",
|
||||
qPrintable(format), help);
|
||||
} else {
|
||||
printf("Format '%s' supports no options.\n", qPrintable(format));
|
||||
qInfo("Format '%s' supports no options.", qPrintable(format));
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
//! [1]
|
||||
|
||||
fprintf(stderr, "Unknown file format '%s'\n", qPrintable(format));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Converter *inconv = nullptr;
|
||||
QString format = parser.value(inputFormatOption);
|
||||
if (format != "auto"_L1) {
|
||||
for (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
if (conv->name() == format) {
|
||||
inconv = conv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inconv) {
|
||||
fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
const Converter *outconv = nullptr;
|
||||
format = parser.value(outputFormatOption);
|
||||
if (format != "auto"_L1) {
|
||||
for (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
if (conv->name() == format) {
|
||||
outconv = conv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!outconv) {
|
||||
fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
qFatal("Unknown file format '%s'", qPrintable(format));
|
||||
}
|
||||
|
||||
//! [2]
|
||||
QStringList files = parser.positionalArguments();
|
||||
QFile input(files.value(0));
|
||||
QFile output(files.value(1));
|
||||
const Converter *inconv = prepareConverter(parser.value(inputFormatOption),
|
||||
Converter::Direction::In, &input);
|
||||
const Converter *outconv = prepareConverter(parser.value(outputFormatOption),
|
||||
Converter::Direction::Out, &output);
|
||||
|
||||
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 (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
if (conv->directions().testFlag(Converter::Direction::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 (const Converter *conv : std::as_const(*availableConverters)) {
|
||||
if (conv->directions().testFlag(Converter::Direction::Out)
|
||||
&& conv->probeFile(&output)) {
|
||||
outconv = conv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now finally perform the conversion
|
||||
// Now finally perform the conversion:
|
||||
QVariant data = inconv->loadFile(&input, outconv);
|
||||
Q_ASSERT_X(outconv, "Converter Tool",
|
||||
Q_ASSERT_X(outconv, "Serialization Converter",
|
||||
"Internal error: converter format did not provide default");
|
||||
outconv->saveFile(&output, data, parser.values(optionOption));
|
||||
return EXIT_SUCCESS;
|
||||
//! [2]
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
static NullConverter nullConverter;
|
||||
Converter *Converter::null = &nullConverter;
|
||||
bool Converter::isNull(const Converter *converter)
|
||||
{
|
||||
return converter == &nullConverter;
|
||||
}
|
||||
|
||||
QString NullConverter::name() const
|
||||
{
|
||||
@ -23,32 +26,12 @@ Converter::Options NullConverter::outputOptions() const
|
||||
return SupportsArbitraryMapKeys;
|
||||
}
|
||||
|
||||
const char *NullConverter::optionsHelp() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NullConverter::probeFile(QIODevice *f) const
|
||||
{
|
||||
Q_UNUSED(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant NullConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||
{
|
||||
Q_UNUSED(f);
|
||||
Q_UNUSED(outputConverter);
|
||||
outputConverter = this;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void NullConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const
|
||||
{
|
||||
if (!options.isEmpty()) {
|
||||
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n",
|
||||
qPrintable(options.first()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown option '%s' to null output. This format has no options.",
|
||||
qPrintable(options.first()));
|
||||
}
|
||||
|
||||
Q_UNUSED(f);
|
||||
|
@ -13,9 +13,6 @@ public:
|
||||
QString name() const override;
|
||||
Directions directions() const override;
|
||||
Options outputOptions() const override;
|
||||
const char *optionsHelp() const override;
|
||||
bool probeFile(QIODevice *f) const override;
|
||||
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
|
||||
void saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const override;
|
||||
};
|
||||
|
@ -54,16 +54,6 @@ Converter::Directions TextConverter::directions() const
|
||||
return Direction::InOut;
|
||||
}
|
||||
|
||||
Converter::Options TextConverter::outputOptions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const char *TextConverter::optionsHelp() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TextConverter::probeFile(QIODevice *f) const
|
||||
{
|
||||
if (QFile *file = qobject_cast<QFile *>(f))
|
||||
@ -98,9 +88,8 @@ void TextConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
const QStringList &options) const
|
||||
{
|
||||
if (!options.isEmpty()) {
|
||||
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n",
|
||||
qPrintable(options.first()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown option '%s' to text output. This format has no options.",
|
||||
qPrintable(options.first()));
|
||||
}
|
||||
|
||||
QTextStream out(f);
|
||||
|
@ -12,8 +12,6 @@ class TextConverter : public Converter
|
||||
public:
|
||||
QString name() const override;
|
||||
Directions directions() const override;
|
||||
Options outputOptions() const override;
|
||||
const char *optionsHelp() const override;
|
||||
bool probeFile(QIODevice *f) const override;
|
||||
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
|
||||
void saveFile(QIODevice *f, const QVariant &contents,
|
||||
|
24
examples/corelib/serialization/convert/variantorderedmap.h
Normal file
24
examples/corelib/serialization/convert/variantorderedmap.h
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef VARIANTORDEREDMAP_H
|
||||
#define VARIANTORDEREDMAP_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
||||
class VariantOrderedMap : public QList<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()});
|
||||
}
|
||||
};
|
||||
|
||||
#endif // VARIANTORDEREDMAP_H
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "xmlconverter.h"
|
||||
#include "variantorderedmap.h"
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QtCborCommon>
|
||||
@ -48,9 +49,8 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -90,9 +90,8 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml,
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
}
|
||||
|
||||
return { key, value };
|
||||
@ -134,9 +133,8 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -153,9 +151,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
if (name == "map"_L1)
|
||||
return mapFromXml(xml, options);
|
||||
if (name != "value"_L1) {
|
||||
fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("%lld:%lld: Invalid XML key '%s'.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
|
||||
}
|
||||
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
@ -168,9 +165,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
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);
|
||||
qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(name.toString()));
|
||||
}
|
||||
|
||||
QStringView text = xml.text();
|
||||
@ -190,9 +186,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
// let's see floating point
|
||||
double d = text.toDouble(&ok);
|
||||
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);
|
||||
qFatal("%lld:%lld: Invalid XML: could not interpret '%s' as a number.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
|
||||
}
|
||||
result = d;
|
||||
}
|
||||
@ -206,9 +201,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} else if (encoding.isEmpty() || encoding == "base64"_L1) {
|
||||
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);
|
||||
qFatal("%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
|
||||
}
|
||||
} else if (type == "string"_L1) {
|
||||
result = text.toString();
|
||||
@ -227,9 +221,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} 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);
|
||||
qFatal("%lld:%lld: Invalid XML: invalid bit string '%s'.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
|
||||
}
|
||||
}
|
||||
ba.resize(n);
|
||||
@ -247,16 +240,14 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
else
|
||||
id = QMetaType::fromName(type.toLatin1()).id();
|
||||
if (id == QMetaType::UnknownType) {
|
||||
fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("%lld:%lld: Invalid XML: unknown type '%s'.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
|
||||
}
|
||||
|
||||
result = text.toString();
|
||||
if (!result.convert(QMetaType(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);
|
||||
qFatal("%lld:%lld: Invalid XML: could not parse content as type '%s'.",
|
||||
xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,9 +256,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
|
||||
} 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);
|
||||
qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(),
|
||||
qPrintable(xml.tokenString()), qPrintable(name.toString()));
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
@ -387,8 +377,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
|
||||
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);
|
||||
qFatal("XML: don't know how to serialize type '%s'.", typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,10 +423,8 @@ QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter)
|
||||
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);
|
||||
}
|
||||
if (xml.hasError())
|
||||
qFatal("XML error: %s", qPrintable(xml.errorString()));
|
||||
|
||||
return v;
|
||||
}
|
||||
@ -452,9 +439,8 @@ void XmlConverter::saveFile(QIODevice *f, const QVariant &contents,
|
||||
} else if (s == "compact=yes"_L1) {
|
||||
compact = true;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s",
|
||||
qPrintable(s), xmlOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
qFatal("Unknown option '%s' to XML output. Valid options are:\n%s",
|
||||
qPrintable(s), xmlOptionHelp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,9 @@
|
||||
/*!
|
||||
\example serialization/savegame
|
||||
\examplecategory {Data Processing & I/O}
|
||||
\title JSON Save Game Example
|
||||
\title Saving and Loading a Game
|
||||
|
||||
\brief The JSON Save Game example demonstrates how to save and load a
|
||||
small game using QJsonDocument, QJsonObject and QJsonArray.
|
||||
\brief How to save and load a game using Qt's JSON or CBOR classes.
|
||||
|
||||
Many games provide save functionality, so that the player's progress through
|
||||
the game can be saved and loaded at a later time. The process of saving a
|
||||
|
Reference in New Issue
Block a user