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