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