MythTV master
xmlSerializer.cpp
Go to the documentation of this file.
1
2// Program Name: xmlSerializer.cpp
3// Created : Dec. 30, 2009
4//
5// Purpose : Serialization Implementation for XML
6//
7// Copyright (c) 2005 David Blain <dblain@mythtv.org>
8//
9// Licensed under the GPL v2 or later, see LICENSE for details
10//
12
13#include "xmlSerializer.h"
15
16#include <algorithm>
17#include <utility>
18
19#include <QMetaClassInfo>
20
21// --------------------------------------------------------------------------
22// This version should be bumped if the serializer code is changed in a way
23// that changes the schema layout of the rendered XML.
24// --------------------------------------------------------------------------
25
26static constexpr const char* XML_SERIALIZER_VERSION { "1.1" };
27
29//
31
32XmlSerializer::XmlSerializer( QIODevice *pDevice, QString sRequestName )
33 : m_pXmlWriter(new QXmlStreamWriter( pDevice )),
34 m_sRequestName(std::move(sRequestName))
35{
36}
37
39//
41
43{
44 if (m_pXmlWriter != nullptr)
45 {
46 delete m_pXmlWriter;
47 m_pXmlWriter = nullptr;
48 }
49}
50
52//
54
56{
57 return "text/xml";
58}
59
61//
63
64void XmlSerializer::BeginSerialize( QString &/*sName*/ )
65{
66 m_pXmlWriter->writeStartDocument( "1.0" );
67// m_pXmlWriter->writeStartElement( m_sRequestName + "Response" );
68}
69
71//
73
75{
76 m_pXmlWriter->writeEndDocument();
77}
78
80//
82
83void XmlSerializer::BeginObject( const QString &sName, const QObject *pObject )
84{
85 m_pXmlWriter->writeStartElement( sName );
86
87 if (m_bIsRoot)
88 {
89 m_pXmlWriter->writeAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
90
91 m_bIsRoot = false;
92 }
93
94 const QMetaObject *pMeta = pObject->metaObject();
95
96 int nIdx = -1;
97
98 if (pMeta)
99 nIdx = pMeta->indexOfClassInfo( "version" );
100
101 if (nIdx >=0)
102 m_pXmlWriter->writeAttribute( "version", pMeta->classInfo( nIdx ).value() );
103
104 m_pXmlWriter->writeAttribute( "serializerVersion", XML_SERIALIZER_VERSION );
105
106}
107
109//
111
112void XmlSerializer::EndObject ( const QString &/*sName*/, const QObject */*pObject*/ )
113{
114 m_pXmlWriter->writeEndElement();
115}
116
118//
120
121void XmlSerializer::AddProperty( const QString &sName,
122 const QVariant &vValue,
123 const QMetaObject *pMetaParent,
124 const QMetaProperty *pMetaProp )
125{
126 m_pXmlWriter->writeStartElement( sName );
127
128 if ((pMetaProp != nullptr) &&
129 (pMetaProp->isEnumType() || pMetaProp->isFlagType()))
130 {
131 RenderEnum ( sName, vValue, pMetaProp );
132 }
133 else
134 {
135 RenderValue( GetContentName( sName, pMetaParent, pMetaProp ), vValue );
136 }
137
138 m_pXmlWriter->writeEndElement();
139}
140
142//
144
145void XmlSerializer::RenderEnum( const QString &/*sName*/ ,
146 const QVariant &vValue,
147 const QMetaProperty *pMetaProp )
148{
149 QString sValue;
150 QMetaEnum metaEnum = pMetaProp->enumerator();
151
152 if (pMetaProp->isFlagType())
153 sValue = metaEnum.valueToKeys( vValue.toInt() );
154 else
155 sValue = metaEnum.valueToKey ( vValue.toInt() );
156
157 // If couldn't convert to enum name, return raw value
158
159 if (sValue.isEmpty())
160 sValue = vValue.toString();
161
162 m_pXmlWriter->writeCharacters( sValue );
163
164}
165
167//
169
170void XmlSerializer::RenderValue( const QString &sName, const QVariant &vValue )
171{
172
173 // -----------------------------------------------------------------------
174 // See if this value is actually a QObject
175 // -----------------------------------------------------------------------
176
177 if ( vValue.canConvert< QObject* >())
178 {
179 const QObject *pObject = vValue.value< QObject* >();
180
181 SerializeObjectProperties( pObject );
182 return;
183 }
184
185 // -----------------------------------------------------------------------
186 // Handle QVariant special cases...
187 // -----------------------------------------------------------------------
188
189#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
190 auto type = static_cast<QMetaType::Type>(vValue.type());
191#else
192 auto type = vValue.typeId();
193#endif
194 switch( type )
195 {
196 case QMetaType::QVariantList:
197 {
198 RenderList( sName, vValue.toList() );
199 break;
200 }
201
202 case QMetaType::QStringList:
203 {
204 RenderStringList( sName, vValue.toStringList() );
205 break;
206 }
207
208 case QMetaType::QVariantMap:
209 {
210 RenderMap( sName, vValue.toMap() );
211 break;
212 }
213
214 case QMetaType::QDateTime:
215 {
216 QDateTime dt( vValue.toDateTime() );
217
218 if (dt.isNull())
219 m_pXmlWriter->writeAttribute( "xsi:nil", "true" );
220
221 m_pXmlWriter->writeCharacters(
223
224 break;
225 }
226
227 default:
228 {
229 m_pXmlWriter->writeCharacters( vValue.toString() );
230 break;
231 }
232 }
233
234}
235
237//
239
240void XmlSerializer::RenderList( const QString &sName, const QVariantList &list )
241{
242// QString sItemName;
243
244 for (const auto& vValue : list)
245 {
246// if (sItemName.isEmpty())
247// sItemName = GetItemName( QMetaType::typeName( vValue.userType() ) );
248
249 m_pXmlWriter->writeStartElement( sName );
250 RenderValue( sName, vValue );
251 m_pXmlWriter->writeEndElement();
252 }
253}
254
256//
258
259void XmlSerializer::RenderStringList( const QString &/*sName*/, const QStringList &list )
260{
261 for (const QString& str : list)
262 {
263 m_pXmlWriter->writeStartElement( "String" );
264 m_pXmlWriter->writeCharacters ( str );
265 m_pXmlWriter->writeEndElement();
266 }
267}
268
270//
272
273void XmlSerializer::RenderMap( const QString &sName, const QVariantMap &map )
274{
275
276 QString sItemName = GetItemName( sName );
277
278 for (auto it = map.cbegin(); it != map.cend(); ++it)
279 {
280 m_pXmlWriter->writeStartElement( sItemName );
281
282 m_pXmlWriter->writeStartElement( "Key" );
283 m_pXmlWriter->writeCharacters( it.key() );
284 m_pXmlWriter->writeEndElement();
285
286 m_pXmlWriter->writeStartElement( "Value" );
287 RenderValue( sItemName, it.value() );
288 m_pXmlWriter->writeEndElement();
289
290/*
291 m_pXmlWriter->writeAttribute ( "key", it.key() );
292 RenderValue( sItemName, it.value() );
293 //m_pXmlWriter->writeCharacters ( it.value().toString() );
294*/
295 m_pXmlWriter->writeEndElement();
296 }
297}
298
300// -=>TODO: There should be a better way to handle this...
301// boy do I miss C#'s Custom Attributes... maybe leverage MOC somehow
303
304QString XmlSerializer::GetItemName( const QString &sName )
305{
306 QString sTypeName( sName );
307
308 if ((sName.length() > 0) && (sName.at(0) == 'Q'))
309 sTypeName = sName.mid( 1 );
310
311 sTypeName.remove( "DTC::" );
312 sTypeName.remove( QChar('*') );
313
314 return sTypeName;
315}
316
318//
320
321QString XmlSerializer::GetContentName( const QString &sName,
322 const QMetaObject *pMetaObject,
323 const QMetaProperty */*pMetaProp*/ )
324{
325 // Try to read Name or TypeName from classinfo metadata.
326
327 int nClassIdx = -1;
328
329 if ( pMetaObject )
330 nClassIdx = pMetaObject->indexOfClassInfo( sName.toLatin1().constData() );
331
332 if (nClassIdx >=0 )
333 {
334 QString sOptionData = pMetaObject->classInfo( nClassIdx ).value();
335 QStringList sOptions = sOptionData.split( ';' );
336
337 QString sNameOption = FindOptionValue( sOptions, "name" );
338
339 if (sNameOption.isEmpty())
340 sNameOption = FindOptionValue( sOptions, "type" );
341
342 if (!sNameOption.isEmpty())
343 return GetItemName( sNameOption );
344 }
345
346 // Neither found, so lets use the type name (slightly modified).
347
348 QString sTypeName( sName );
349
350 if ((sName.length() > 0) && (sName.at(0) == 'Q'))
351 sTypeName = sName.mid( 1 );
352
353 sTypeName.remove( "DTC::" );
354 sTypeName.remove( QChar('*') );
355
356 return sTypeName;
357}
358
360//
362
363QString XmlSerializer::FindOptionValue( const QStringList &sOptions, const QString &sName )
364{
365 QString sKey = sName + "=";
366
367 auto hasKey = [&sKey](const QString& o) { return o.startsWith( sKey ); };
368 auto it = std::ranges::find_if(std::as_const(sOptions), hasKey);
369 if (it != sOptions.cend())
370 return (*it).mid( sKey.length() );
371
372 return {};
373}
void SerializeObjectProperties(const QObject *pObject)
Definition: serializer.cpp:103
void AddProperty(const QString &sName, const QVariant &vValue, const QMetaObject *pMetaParent, const QMetaProperty *pMetaProp) override
static QString GetContentName(const QString &sName, const QMetaObject *pMetaObject, const QMetaProperty *pMetaProp)
QXmlStreamWriter * m_pXmlWriter
Definition: xmlSerializer.h:37
void RenderEnum(const QString &sName, const QVariant &vValue, const QMetaProperty *pMetaProp)
void RenderMap(const QString &sName, const QVariantMap &map)
void EndObject(const QString &sName, const QObject *pObject) override
QString GetContentType() override
static QString FindOptionValue(const QStringList &sOptions, const QString &sName)
void RenderValue(const QString &sName, const QVariant &vValue)
static QString GetItemName(const QString &sName)
void BeginSerialize(QString &sName) override
virtual ~XmlSerializer()
void EndSerialize() override
XmlSerializer(QIODevice *pDevice, QString sRequestName)
void RenderStringList(const QString &sName, const QStringList &list)
void RenderList(const QString &sName, const QVariantList &list)
void BeginObject(const QString &sName, const QObject *pObject) override
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
static constexpr const char * XML_SERIALIZER_VERSION