Rework documentation of the serialization converter example
The documentation was previously code-heavy and short on exposition, while focusing almost entirely on the CBOR converters. Prune most of the CBOR code quotes, shuffle some snippet markers, add and remove others and rewrite the main text. Shift focus to the base-class for converters and how it's used by main(). Retain relative focus on the CBOR converters, as they are relatively full-featured hence illustrate more than the others do, and replace the sequence of single-line sections about the others with a section collecting all into a table and saying some general things about them. Pick-to: 6.6 6.5 Task-number: QTBUG-111228 Change-Id: I8d41f25c165eb1a7ba20cb68aee6ab6b2fd050f8 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
1022b0d111
commit
1940f0c7be
@ -59,9 +59,9 @@ QT_END_NAMESPACE
|
|||||||
// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we
|
// 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.
|
// have our own set of converter functions so we can keep the keys properly.
|
||||||
|
|
||||||
|
//! [0]
|
||||||
static QVariant convertCborValue(const QCborValue &value);
|
static QVariant convertCborValue(const QCborValue &value);
|
||||||
|
|
||||||
//! [0]
|
|
||||||
static QVariant convertCborMap(const QCborMap &map)
|
static QVariant convertCborMap(const QCborMap &map)
|
||||||
{
|
{
|
||||||
VariantOrderedMap result;
|
VariantOrderedMap result;
|
||||||
@ -89,8 +89,9 @@ static QVariant convertCborValue(const QCborValue &value)
|
|||||||
return value.toVariant();
|
return value.toVariant();
|
||||||
}
|
}
|
||||||
//! [0]
|
//! [0]
|
||||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
|
||||||
//! [1]
|
//! [1]
|
||||||
|
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||||
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
|
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
|
||||||
{
|
{
|
||||||
if (v.userType() == QMetaType::QVariantList) {
|
if (v.userType() == QMetaType::QVariantList) {
|
||||||
@ -208,7 +209,6 @@ bool CborConverter::probeFile(QIODevice *f) const
|
|||||||
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
|
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! [2]
|
|
||||||
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
|
||||||
{
|
{
|
||||||
const char *ptr = nullptr;
|
const char *ptr = nullptr;
|
||||||
@ -242,11 +242,9 @@ QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter
|
|||||||
return contents.toVariant();
|
return contents.toVariant();
|
||||||
return convertCborValue(contents);
|
return convertCborValue(contents);
|
||||||
}
|
}
|
||||||
//! [2]
|
|
||||||
//! [3]
|
|
||||||
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
|
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
|
||||||
{
|
{
|
||||||
//! [3]
|
|
||||||
bool useSignature = true;
|
bool useSignature = true;
|
||||||
bool useIntegers = true;
|
bool useIntegers = true;
|
||||||
enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
|
enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
|
||||||
@ -304,7 +302,7 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
|
|||||||
qFatal("Unknown CBOR format option '%s'. Valid options are:\n%s",
|
qFatal("Unknown CBOR format option '%s'. Valid options are:\n%s",
|
||||||
qPrintable(s), cborOptionHelp);
|
qPrintable(s), cborOptionHelp);
|
||||||
}
|
}
|
||||||
//! [4]
|
|
||||||
QCborValue v =
|
QCborValue v =
|
||||||
convertFromVariant(contents,
|
convertFromVariant(contents,
|
||||||
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
|
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
|
||||||
@ -321,4 +319,3 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
|
|||||||
opts |= QCborValue::UseFloat16;
|
opts |= QCborValue::UseFloat16;
|
||||||
v.toCbor(writer, opts);
|
v.toCbor(writer, opts);
|
||||||
}
|
}
|
||||||
//! [4]
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "converter.h"
|
#include "converter.h"
|
||||||
|
|
||||||
|
//! [0]
|
||||||
Converter::Converter()
|
Converter::Converter()
|
||||||
{
|
{
|
||||||
converters().append(this);
|
converters().append(this);
|
||||||
@ -23,6 +24,7 @@ const QList<const Converter *> &Converter::allConverters()
|
|||||||
{
|
{
|
||||||
return converters();
|
return converters();
|
||||||
}
|
}
|
||||||
|
//! [0]
|
||||||
|
|
||||||
// Some virtual methods that Converter classes needn't override, when not relevant:
|
// Some virtual methods that Converter classes needn't override, when not relevant:
|
||||||
Converter::Options Converter::outputOptions() const { return {}; }
|
Converter::Options Converter::outputOptions() const { return {}; }
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
//! [0]
|
||||||
class Converter
|
class Converter
|
||||||
{
|
{
|
||||||
static QList<const Converter *> &converters();
|
static QList<const Converter *> &converters();
|
||||||
@ -39,5 +40,6 @@ public:
|
|||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
|
||||||
|
//! [0]
|
||||||
|
|
||||||
#endif // CONVERTER_H
|
#endif // CONVERTER_H
|
||||||
|
@ -7,76 +7,150 @@
|
|||||||
\meta tag {network}
|
\meta tag {network}
|
||||||
\title Serialization Converter
|
\title Serialization Converter
|
||||||
|
|
||||||
\brief The Convert example demonstrates how to convert between different
|
\brief How to convert between different serialization formats.
|
||||||
serialization formats.
|
|
||||||
|
|
||||||
The Convert example converts between the serialization formats JSON, CBOR,
|
This example converts between JSON, CBOR, XML, QDataStream and some simple
|
||||||
XML, QDataStream and text. It can also auto detect the format being used.
|
text formats. It can auto-detect the format being used, or be told which
|
||||||
Not all formats support both input and output, and they have different
|
format to use. Not all formats support both input and output, and they have
|
||||||
sets of which types they support. QDataStream and XML are the richest,
|
different sets of which content datatypes they support. QDataStream and XML
|
||||||
followed by CBOR, then JSON, and then the plain text one.
|
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
|
\image convert.png
|
||||||
|
|
||||||
|
\sa {Parsing and displaying CBOR data}, {JSON Save Game}
|
||||||
|
|
||||||
\section1 The Converter Class
|
\section1 The Converter Class
|
||||||
|
|
||||||
The Converter class is the abstract superclass for all the converters to
|
The Converter class is the abstract superclass for all the converters to and
|
||||||
and from all the formats. They all convert to and from the QVariant class,
|
from all the formats. They all convert from or to the QVariant class, which
|
||||||
which is used to represent all the datastructures internally.
|
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()
|
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,
|
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
|
or both. These enable the main program to report what converters are
|
||||||
and query which options are used by the different converters. The
|
available in its help text for the command-line options to select input and
|
||||||
probeFile() function is used to determine if a file has the same file
|
output formats.
|
||||||
format as the converter. The loadFile() function deserializes the given
|
|
||||||
file, while the saveFile() serializes to the given file.
|
|
||||||
|
|
||||||
\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
|
There is also a CborDiagnosticDumper class to output in CBOR diagnostic
|
||||||
notation. That is similar to JSON, but not exactly, because it allows
|
notation. It does not support loading data. The form of its output can be
|
||||||
displaying the contents of a CBOR stream losslessly, while a conversion
|
configured using two options. One selects whether to use the (more verbose)
|
||||||
to JSON is lossy.
|
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
|
\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
|
The convertFromVariant() function is used to convert a QVariant to a
|
||||||
QCborValue.
|
QCborValue for output by the \c saveFile() of either class.
|
||||||
\snippet serialization/convert/cborconverter.cpp 1
|
|
||||||
|
|
||||||
A CBOR-file is written using the saveFile() function.
|
\snippet serialization/convert/cborconverter.cpp 1
|
||||||
\snippet serialization/convert/cborconverter.cpp 3
|
|
||||||
\snippet serialization/convert/cborconverter.cpp 4
|
|
||||||
|
|
||||||
\sa {CBOR Support in Qt}
|
\sa {CBOR Support in Qt}
|
||||||
|
|
||||||
\section1 The DataStreamConverter Class
|
\section1 The convert program
|
||||||
|
|
||||||
The DataStreamConverter class is used to serialize to and from the
|
The \c main() function sets up a \l QApplication and a \l QCommandLineParser
|
||||||
QDataStream format. There is also the DebugTextDumper class for outputting
|
to make sense of the options the user has specified and provide help if the
|
||||||
the data lossless in a non-standardized human readable format.
|
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.
|
\snippet serialization/convert/main.cpp 2
|
||||||
\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.
|
|
||||||
*/
|
*/
|
||||||
|
@ -61,6 +61,7 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
//! [0]
|
||||||
QStringList inputFormats;
|
QStringList inputFormats;
|
||||||
QStringList outputFormats;
|
QStringList outputFormats;
|
||||||
for (const Converter *conv : Converter::allConverters()) {
|
for (const Converter *conv : Converter::allConverters()) {
|
||||||
@ -71,6 +72,7 @@ int main(int argc, char *argv[])
|
|||||||
if (direction.testFlag(Converter::Direction::Out))
|
if (direction.testFlag(Converter::Direction::Out))
|
||||||
outputFormats << name;
|
outputFormats << name;
|
||||||
}
|
}
|
||||||
|
//! [0]
|
||||||
inputFormats.sort();
|
inputFormats.sort();
|
||||||
outputFormats.sort();
|
outputFormats.sort();
|
||||||
inputFormats.prepend("auto"_L1);
|
inputFormats.prepend("auto"_L1);
|
||||||
@ -116,6 +118,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (parser.isSet(formatOptionsOption)) {
|
if (parser.isSet(formatOptionsOption)) {
|
||||||
QString format = parser.value(formatOptionsOption);
|
QString format = parser.value(formatOptionsOption);
|
||||||
|
//! [1]
|
||||||
for (const Converter *conv : Converter::allConverters()) {
|
for (const Converter *conv : Converter::allConverters()) {
|
||||||
if (conv->name() == format) {
|
if (conv->name() == format) {
|
||||||
const char *help = conv->optionsHelp();
|
const char *help = conv->optionsHelp();
|
||||||
@ -128,10 +131,12 @@ int main(int argc, char *argv[])
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//! [1]
|
||||||
|
|
||||||
qFatal("Unknown file format '%s'", qPrintable(format));
|
qFatal("Unknown file format '%s'", qPrintable(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [2]
|
||||||
QStringList files = parser.positionalArguments();
|
QStringList files = parser.positionalArguments();
|
||||||
QFile input(files.value(0));
|
QFile input(files.value(0));
|
||||||
QFile output(files.value(1));
|
QFile output(files.value(1));
|
||||||
@ -146,4 +151,5 @@ int main(int argc, char *argv[])
|
|||||||
"Internal error: converter format did not provide default");
|
"Internal error: converter format did not provide default");
|
||||||
outconv->saveFile(&output, data, parser.values(optionOption));
|
outconv->saveFile(&output, data, parser.values(optionOption));
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
//! [2]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user