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