mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-08 02:17:43 +08:00
qt 6.5.1 original
This commit is contained in:
36
examples/corelib/serialization/convert/CMakeLists.txt
Normal file
36
examples/corelib/serialization/convert/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(convert LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/convert")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(convert
|
||||
cborconverter.cpp cborconverter.h
|
||||
converter.h
|
||||
datastreamconverter.cpp datastreamconverter.h
|
||||
jsonconverter.cpp jsonconverter.h
|
||||
main.cpp
|
||||
nullconverter.cpp nullconverter.h
|
||||
textconverter.cpp textconverter.h
|
||||
xmlconverter.cpp xmlconverter.h
|
||||
)
|
||||
|
||||
target_link_libraries(convert PRIVATE
|
||||
Qt6::Core
|
||||
)
|
||||
|
||||
install(TARGETS convert
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
336
examples/corelib/serialization/convert/cborconverter.cpp
Normal file
336
examples/corelib/serialization/convert/cborconverter.cpp
Normal file
@ -0,0 +1,336 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "cborconverter.h"
|
||||
|
||||
#include <QCborStreamReader>
|
||||
#include <QCborStreamWriter>
|
||||
#include <QCborMap>
|
||||
#include <QCborArray>
|
||||
#include <QCborValue>
|
||||
#include <QDataStream>
|
||||
#include <QFloat16>
|
||||
#include <QFile>
|
||||
#include <QMetaType>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static CborConverter cborConverter;
|
||||
static CborDiagnosticDumper cborDiagnosticDumper;
|
||||
|
||||
static const char cborOptionHelp[] =
|
||||
"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, 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);
|
||||
|
||||
//! [0]
|
||||
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();
|
||||
}
|
||||
//! [0]
|
||||
enum TrimFloatingPoint { Double, Float, Float16 };
|
||||
//! [1]
|
||||
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
|
||||
{
|
||||
if (v.userType() == QMetaType::QVariantList) {
|
||||
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 = qvariant_cast<VariantOrderedMap>(v);
|
||||
QCborMap map;
|
||||
for (const auto &pair : m)
|
||||
map.insert(convertFromVariant(pair.first, fpTrimming),
|
||||
convertFromVariant(pair.second, fpTrimming));
|
||||
return map;
|
||||
}
|
||||
|
||||
if (v.userType() == QMetaType::Double && fpTrimming != Double) {
|
||||
float f = float(v.toDouble());
|
||||
if (fpTrimming == Float16)
|
||||
return float(qfloat16(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
return QCborValue::fromVariant(v);
|
||||
}
|
||||
//! [1]
|
||||
|
||||
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)
|
||||
<< Qt::endl;
|
||||
}
|
||||
|
||||
CborConverter::CborConverter()
|
||||
{
|
||||
qRegisterMetaType<QCborTag>();
|
||||
}
|
||||
|
||||
QString CborConverter::name()
|
||||
{
|
||||
return "cbor";
|
||||
}
|
||||
|
||||
Converter::Direction CborConverter::directions()
|
||||
{
|
||||
return InOut;
|
||||
}
|
||||
|
||||
Converter::Options CborConverter::outputOptions()
|
||||
{
|
||||
return SupportsArbitraryMapKeys;
|
||||
}
|
||||
|
||||
const char *CborConverter::optionsHelp()
|
||||
{
|
||||
return cborOptionHelp;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//! [2]
|
||||
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);
|
||||
}
|
||||
//! [2]
|
||||
//! [3]
|
||||
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
|
||||
{
|
||||
//! [3]
|
||||
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), cborOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
//! [4]
|
||||
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);
|
||||
}
|
||||
//! [4]
|
38
examples/corelib/serialization/convert/cborconverter.h
Normal file
38
examples/corelib/serialization/convert/cborconverter.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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
|
28
examples/corelib/serialization/convert/convert.pro
Normal file
28
examples/corelib/serialization/convert/convert.pro
Normal file
@ -0,0 +1,28 @@
|
||||
QT += core
|
||||
QT -= gui
|
||||
|
||||
TARGET = convert
|
||||
CONFIG += cmdline
|
||||
|
||||
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
|
57
examples/corelib/serialization/convert/converter.h
Normal file
57
examples/corelib/serialization/convert/converter.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef CONVERTER_H
|
||||
#define CONVERTER_H
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QPair>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
#include <QList>
|
||||
|
||||
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)
|
||||
|
||||
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
|
225
examples/corelib/serialization/convert/datastreamconverter.cpp
Normal file
225
examples/corelib/serialization/convert/datastreamconverter.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "datastreamconverter.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
|
||||
static const char dataStreamOptionHelp[] =
|
||||
"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 == QMetaType::QVariantMap) {
|
||||
const auto map = (type == QMetaType::QVariantMap) ?
|
||||
VariantOrderedMap(v.toMap()) : qvariant_cast<VariantOrderedMap>(v);
|
||||
|
||||
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 == QMetaType::QVariantList) {
|
||||
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>();
|
||||
}
|
||||
|
||||
QString DataStreamConverter::name()
|
||||
{
|
||||
return QStringLiteral("datastream");
|
||||
}
|
||||
|
||||
Converter::Direction DataStreamConverter::directions()
|
||||
{
|
||||
return InOut;
|
||||
}
|
||||
|
||||
Converter::Options DataStreamConverter::outputOptions()
|
||||
{
|
||||
return SupportsArbitraryMapKeys;
|
||||
}
|
||||
|
||||
const char *DataStreamConverter::optionsHelp()
|
||||
{
|
||||
return dataStreamOptionHelp;
|
||||
}
|
||||
|
||||
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), dataStreamOptionHelp);
|
||||
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;
|
||||
}
|
38
examples/corelib/serialization/convert/datastreamconverter.h
Normal file
38
examples/corelib/serialization/convert/datastreamconverter.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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
|
BIN
examples/corelib/serialization/convert/doc/images/convert.png
Normal file
BIN
examples/corelib/serialization/convert/doc/images/convert.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
81
examples/corelib/serialization/convert/doc/src/convert.qdoc
Normal file
81
examples/corelib/serialization/convert/doc/src/convert.qdoc
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example serialization/convert
|
||||
\examplecategory {Input/Output}
|
||||
\title Convert Example
|
||||
|
||||
\brief The Convert example demonstrates 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.
|
||||
|
||||
\image convert.png
|
||||
|
||||
\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 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.
|
||||
|
||||
\section1 The CborConverter Class
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
A CBOR-file is written using the saveFile() function.
|
||||
\snippet serialization/convert/cborconverter.cpp 3
|
||||
\snippet serialization/convert/cborconverter.cpp 4
|
||||
|
||||
\sa {CBOR Support in Qt}
|
||||
|
||||
\section1 The DataStreamConverter Class
|
||||
|
||||
The DataStreamConverter class is used to serialize to and from the
|
||||
QDataStream format. There is also the DataStreamDumper class for outputting
|
||||
the data lossless in a non-standardized human readable format.
|
||||
|
||||
\section1 The JsonConverter Class
|
||||
|
||||
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.
|
||||
*/
|
106
examples/corelib/serialization/convert/jsonconverter.cpp
Normal file
106
examples/corelib/serialization/convert/jsonconverter.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "jsonconverter.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
static JsonConverter jsonConverter;
|
||||
|
||||
static const char jsonOptionHelp[] =
|
||||
"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 jsonOptionHelp;
|
||||
}
|
||||
|
||||
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), jsonOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
f->write(convertFromVariant(contents).toJson(format));
|
||||
}
|
25
examples/corelib/serialization/convert/jsonconverter.h
Normal file
25
examples/corelib/serialization/convert/jsonconverter.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
#endif // JSONCONVERTER_H
|
187
examples/corelib/serialization/convert/main.cpp
Normal file
187
examples/corelib/serialization/convert/main.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "converter.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static QList<Converter *> *availableConverters;
|
||||
|
||||
Converter::Converter()
|
||||
{
|
||||
if (!availableConverters)
|
||||
availableConverters = new QList<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 : std::as_const(*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 : std::as_const(*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 : 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;
|
||||
}
|
||||
}
|
||||
|
||||
Converter *outconv = nullptr;
|
||||
format = parser.value(outputFormatOption);
|
||||
if (format != "auto") {
|
||||
for (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;
|
||||
}
|
||||
}
|
||||
|
||||
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 : std::as_const(*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 : std::as_const(*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;
|
||||
}
|
52
examples/corelib/serialization/convert/nullconverter.cpp
Normal file
52
examples/corelib/serialization/convert/nullconverter.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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);
|
||||
}
|
22
examples/corelib/serialization/convert/nullconverter.h
Normal file
22
examples/corelib/serialization/convert/nullconverter.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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
|
113
examples/corelib/serialization/convert/textconverter.cpp
Normal file
113
examples/corelib/serialization/convert/textconverter.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "textconverter.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
static void dumpVariant(QTextStream &out, const QVariant &v)
|
||||
{
|
||||
switch (v.userType()) {
|
||||
case QMetaType::QVariantList: {
|
||||
const QVariantList list = v.toList();
|
||||
for (const QVariant &item : list)
|
||||
dumpVariant(out, item);
|
||||
break;
|
||||
}
|
||||
|
||||
case QMetaType::QString: {
|
||||
const QStringList list = v.toStringList();
|
||||
for (const QString &s : list)
|
||||
out << s << Qt::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
case QMetaType::QVariantMap: {
|
||||
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)" << Qt::endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
out << v.toString() << Qt::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;
|
23
examples/corelib/serialization/convert/textconverter.h
Normal file
23
examples/corelib/serialization/convert/textconverter.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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
|
468
examples/corelib/serialization/convert/xmlconverter.cpp
Normal file
468
examples/corelib/serialization/convert/xmlconverter.cpp
Normal file
@ -0,0 +1,468 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "xmlconverter.h"
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QtCborCommon>
|
||||
#include <QFile>
|
||||
#include <QFloat16>
|
||||
#include <QMetaType>
|
||||
#include <QRegularExpression>
|
||||
#include <QUrl>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
static const char xmlOptionHelp[] =
|
||||
"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.name() == QLatin1String("list"))) {
|
||||
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.name() == QLatin1String("entry"))) {
|
||||
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.name() == QLatin1String("map"))) {
|
||||
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)
|
||||
{
|
||||
QStringView 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();
|
||||
QStringView 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);
|
||||
}
|
||||
|
||||
QStringView 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();
|
||||
QStringView 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 = QMetaType::UnknownType;
|
||||
if (type == QLatin1String("datetime"))
|
||||
id = QMetaType::QDateTime;
|
||||
else if (type == QLatin1String("url"))
|
||||
id = QMetaType::QUrl;
|
||||
else if (type == QLatin1String("uuid"))
|
||||
id = QMetaType::QUuid;
|
||||
else if (type == QLatin1String("regex"))
|
||||
id = QMetaType::QRegularExpression;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 == QMetaType::QVariantList) {
|
||||
QVariantList list = v.toList();
|
||||
xml.writeStartElement("list");
|
||||
for (const QVariant &v : list)
|
||||
variantToXml(xml, v);
|
||||
xml.writeEndElement();
|
||||
} else if (type == QMetaType::QVariantMap || type == qMetaTypeId<VariantOrderedMap>()) {
|
||||
const VariantOrderedMap map = (type == QMetaType::QVariantMap) ?
|
||||
VariantOrderedMap(v.toMap()) :
|
||||
qvariant_cast<VariantOrderedMap>(v);
|
||||
|
||||
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(qvariant_cast<qfloat16>(v))));
|
||||
} else if (type == qMetaTypeId<QCborSimpleType>()) {
|
||||
xml.writeAttribute(typeString, "CBOR simple type");
|
||||
xml.writeCharacters(QString::number(int(qvariant_cast<QCborSimpleType>(v))));
|
||||
} else {
|
||||
// does this convert to string?
|
||||
const char *typeName = v.typeName();
|
||||
QVariant copy = v;
|
||||
if (copy.convert(QMetaType(QMetaType::QString))) {
|
||||
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 xmlOptionHelp;
|
||||
}
|
||||
|
||||
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), xmlOptionHelp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
QXmlStreamWriter xml(f);
|
||||
xml.setAutoFormatting(!compact);
|
||||
xml.writeStartDocument();
|
||||
variantToXml(xml, contents);
|
||||
xml.writeEndDocument();
|
||||
}
|
22
examples/corelib/serialization/convert/xmlconverter.h
Normal file
22
examples/corelib/serialization/convert/xmlconverter.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2018 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#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
|
Reference in New Issue
Block a user