MythTV master
mythxmlplistserialiser.cpp
Go to the documentation of this file.
1// C++ headers
2#include <algorithm>
3
4// Qt
5#include <QChar> // Fix Qt6 GCC SFINAE warning
6#include <QMetaProperty>
7#if QT_VERSION < QT_VERSION_CHECK(6,11,0)
8#include <QSequentialIterable>
9#else
10#include <QMetaSequence>
11#endif
12
13// MythTV
14#include "mythdate.h"
15#include "http/mythhttpdata.h"
17
18MythXMLPListSerialiser::MythXMLPListSerialiser(const QString& Name, const QVariant& Value)
19{
20 m_writer.setDevice(&m_buffer);
21 m_writer.setAutoFormatting(true);
22 m_writer.setAutoFormattingIndent(4);
23 m_writer.writeStartDocument("1.0");
24 m_writer.writeDTD(R"(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">)");
25 m_writer.writeStartElement("plist");
26 m_writer.writeAttribute("version", "1.0");
27 m_writer.writeStartElement("dict");
28 m_writer.writeTextElement("key", "serializerversion");
29 m_writer.writeTextElement("string", XML_PLIST_SERIALIZER_VERSION);
30 QString name = Name;
31 if (name.startsWith("V2"))
32 name.remove(0,2);
33 AddObject(name, Value);
34 m_writer.writeEndElement();
35 m_writer.writeEndElement();
36 m_writer.writeEndDocument();
37}
38
39void MythXMLPListSerialiser::AddObject(const QString& Name, const QVariant& Value)
40{
41 auto * object = Value.value<QObject*>();
42 AddValue(object ? GetContentName(Name, object->metaObject()) : Name, Value);
43}
44
45void MythXMLPListSerialiser::AddValue(const QString& Name, const QVariant& Value, bool NeedKey)
46{
47 auto * object = Value.value<QObject*>();
48 if (object)
49 {
50 QVariant isNull = object->property("isNull");
51 if (isNull.toBool())
52 return;
53 AddQObject(Name, object);
54 return;
55 }
56
57 switch (
58#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
59 static_cast<QMetaType::Type>(Value.type())
60#else
61 static_cast<QMetaType::Type>(Value.typeId())
62#endif
63 )
64 {
65 case QMetaType::QStringList: AddStringList(Name, Value); break;
66 case QMetaType::QVariantList: AddList(Name, Value.toList()); break;
67 case QMetaType::QVariantMap: AddMap(Name, Value.toMap()); break;
68 case QMetaType::UInt:
69 case QMetaType::ULongLong:
70 {
71 if (NeedKey)
72 m_writer.writeTextElement("key", Name);
73 m_writer.writeTextElement("integer", QString::number(Value.toULongLong()));
74 break;
75 }
76
77 case QMetaType::Int:
78 case QMetaType::LongLong:
79 case QMetaType::Double:
80 {
81 if (NeedKey)
82 m_writer.writeTextElement("key", Name);
83 m_writer.writeTextElement("real", QString("%1").arg(Value.toDouble(), 0, 'f', 6));
84 break;
85 }
86 case QMetaType::Float:
87 {
88 if (NeedKey)
89 m_writer.writeTextElement("key", Name);
90 m_writer.writeTextElement("real", QString("%1").arg(Value.toFloat(), 0, 'f', 6));
91 break;
92 }
93 case QMetaType::QByteArray:
94 {
95 if (!Value.toByteArray().isNull())
96 {
97 if (NeedKey)
98 m_writer.writeTextElement("key", Name);
99 m_writer.writeTextElement("data", Value.toByteArray().toBase64().data());
100 }
101 break;
102 }
103 case QMetaType::Bool:
104 {
105 if (NeedKey)
106 m_writer.writeTextElement("key", Name);
107 m_writer.writeEmptyElement(Value.toBool() ? "true" : "false");
108 break;
109 }
110 case QMetaType::QDateTime:
111 if (Value.toDateTime().isValid())
112 {
113 if (NeedKey)
114 m_writer.writeTextElement("key", Name);
115 m_writer.writeTextElement("date", Value.toDateTime().toUTC().toString("yyyy-MM-ddThh:mm:ssZ"));
116 }
117 break;
118 case QMetaType::QString:
119 default:
120 if (NeedKey)
121 m_writer.writeTextElement("key", Name);
122 m_writer.writeTextElement("string", Value.toString());
123 }
124}
125
126void MythXMLPListSerialiser::AddQObject(const QString &Name, const QObject* Object)
127{
128 if (!Object)
129 return;
130
131 m_writer.writeTextElement("key", Name);
132 m_writer.writeStartElement("dict");
133
134 const auto * meta = Object->metaObject();
135 if (int index = meta->indexOfClassInfo("Version"); index >= 0)
136 {
137 m_writer.writeTextElement("key", "version");
138 m_writer.writeTextElement("string", meta->classInfo(index).value());
139 }
140
141 int count = meta->propertyCount();
142 for (int index = 0; index < count; ++index )
143 {
144 QMetaProperty metaproperty = meta->property(index);
145 if (
146#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
147 metaproperty.isUser(Object)
148#else
149 metaproperty.isUser()
150#endif
151 )
152 {
153 const char *rawname = metaproperty.name();
154 QString name(rawname);
155 if (name.compare("objectName") == 0)
156 continue;
157 AddProperty(name, Object->property(rawname), meta, &metaproperty);
158 }
159 }
160
161 m_writer.writeEndElement();
162}
163
164void MythXMLPListSerialiser::AddProperty(const QString& Name, const QVariant& Value,
165 const QMetaObject* MetaObject, const QMetaProperty *MetaProperty)
166{
167 QString value;
168 // Enum ?
169 if (MetaProperty && (MetaProperty->isEnumType() || MetaProperty->isFlagType()))
170 {
171 QMetaEnum metaEnum = MetaProperty->enumerator();
172 value = MetaProperty->isFlagType() ? metaEnum.valueToKeys(Value.toInt()).constData() :
173 metaEnum.valueToKey(Value.toInt());
174 }
175 AddValue(GetContentName(Name, MetaObject), value.isEmpty() ? Value : value);
176}
177
178void MythXMLPListSerialiser::AddStringList(const QString& Name, const QVariant& Values)
179{
180 m_writer.writeTextElement("key", Name);
181 m_writer.writeStartElement("array");
182#if QT_VERSION < QT_VERSION_CHECK(6,11,0)
183 auto values = Values.value<QSequentialIterable>();
184#else
185 auto values = Values.value<QMetaSequence::Iterable>();
186#endif
187 for (const auto & value : values)
188 m_writer.writeTextElement("string", value.toString());
189 m_writer.writeEndElement();
190}
191
192void MythXMLPListSerialiser::AddList(const QString& Name, const QVariantList &Values)
193{
194 bool array = true;
195 if (!Values.isEmpty())
196 {
197#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
198 auto type = static_cast<QMetaType::Type>(Values.front().type());
199 auto typesEqual = [type](const QVariant& value)
200 { return (static_cast<QMetaType::Type>(value.type()) == type); };
201#else
202 auto type = static_cast<QMetaType::Type>(Values.front().typeId());
203 auto typesEqual = [type](const QVariant& value)
204 { return (static_cast<QMetaType::Type>(value.typeId()) == type); };
205#endif
206 array = std::ranges::all_of(std::as_const(Values), typesEqual);
207 }
208
209 QString name = GetItemName(Name);
210 m_writer.writeTextElement("key", name);
211 m_writer.writeStartElement(array ? "array" : "dict");
212
213 for (const auto& variant : Values)
214 AddValue(name, variant, !array);
215 m_writer.writeEndElement();
216}
217
218void MythXMLPListSerialiser::AddMap(const QString& Name, const QVariantMap& Map)
219{
220 QString itemname = GetItemName(Name);
221 m_writer.writeTextElement("key", itemname);
222 m_writer.writeStartElement("dict");
223 for (auto it = Map.cbegin(); it != Map.cend(); ++it)
224 AddValue(it.key(), it.value());
225 m_writer.writeEndElement();
226}
227
229{
230 QString name = Name.startsWith("Q") ? Name.mid(1) : Name;
231 name.remove("DTC::");
232 if (name.startsWith("V2"))
233 name.remove(0,2);
234 name.remove(QChar('*'));
235 return name;
236}
237
239QString MythXMLPListSerialiser::GetContentName(const QString& Name, const QMetaObject* MetaObject)
240{
241 // Try to read Name or TypeName from classinfo metadata.
242 if (int index = MetaObject ? MetaObject->indexOfClassInfo(Name.toLatin1().constData()) : -1; index >= 0)
243 {
244 QStringList infos = QString(MetaObject->classInfo(index).value()).split(';', Qt::SkipEmptyParts);
245 QString type; // fallback
246 for (const QString &info : std::as_const(infos))
247 {
248 if (info.startsWith(QStringLiteral("name=")))
249 if (auto name = info.mid(5).trimmed(); !name.isEmpty())
250 return GetItemName(name);
251 if (info.startsWith(QStringLiteral("type=")))
252 type = info.mid(5).trimmed();
253 }
254 if (!type.isEmpty())
255 return GetItemName(type);
256 }
257
258 // Neither found, so lets use the type name (slightly modified).
259 return GetItemName(Name);
260}
261
void AddProperty(const QString &Name, const QVariant &Value, const QMetaObject *MetaObject, const QMetaProperty *MetaProperty)
static QString GetItemName(const QString &Name)
void AddStringList(const QString &Name, const QVariant &Values)
void AddList(const QString &Name, const QVariantList &Values)
void AddQObject(const QString &Name, const QObject *Object)
void AddObject(const QString &Name, const QVariant &Value)
MythXMLPListSerialiser(const QString &Name, const QVariant &Value)
void AddValue(const QString &Name, const QVariant &Value, bool NeedKey=true)
static QString GetContentName(const QString &Name, const QMetaObject *MetaObject)
FIXME We shouldn't be doing this on the fly.
void AddMap(const QString &Name, const QVariantMap &Map)
#define XML_PLIST_SERIALIZER_VERSION
dictionary info
Definition: azlyrics.py:7