Skip to content

Commit

Permalink
linmythupnp: Add an XML Plist serializer.
Browse files Browse the repository at this point in the history
This should help skip a conversion stage when processing responses on
iOS/OS X devices. If it proves to be beneficial, I'll add a binary plist
version which will improve performance on iOS devices - especially for
large guide requests etc.
  • Loading branch information
Mark Kendall authored and Robert McNamara committed Feb 11, 2012
1 parent 7fa97e0 commit 5a697eb
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 0 deletions.
3 changes: 3 additions & 0 deletions mythtv/libs/libmythupnp/httprequest.cpp
Expand Up @@ -54,6 +54,7 @@
#include "serializers/xmlSerializer.h"
#include "serializers/soapSerializer.h"
#include "serializers/jsonSerializer.h"
#include "serializers/xmlplistSerializer.h"

#ifndef O_LARGEFILE
#define O_LARGEFILE 0
Expand Down Expand Up @@ -1414,6 +1415,8 @@ Serializer *HTTPRequest::GetSerializer()
else if (sAccept.contains( "text/javascript", Qt::CaseInsensitive ))
pSerializer = (Serializer *)new JSONSerializer(&m_response,
m_sMethod);
else if (sAccept.contains( "text/x-apple-plist+xml", Qt::CaseInsensitive ))
pSerializer = (Serializer *)new XmlPListSerializer(&m_response);
}

// Default to XML
Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmythupnp/libmythupnp.pro
Expand Up @@ -29,6 +29,7 @@ HEADERS += servicehost.h wsdl.h htmlserver.h serverSideScripting.h

HEADERS += serializers/serializer.h serializers/xmlSerializer.h
HEADERS += serializers/jsonSerializer.h serializers/soapSerializer.h
HEADERS += serializers/xmlplistSerializer.h

SOURCES += mmulticastsocketdevice.cpp
SOURCES += httprequest.cpp upnp.cpp ssdp.cpp taskqueue.cpp upnputil.cpp
Expand All @@ -42,6 +43,7 @@ SOURCES += servicehost.cpp wsdl.cpp upnpsubscription.cpp

SOURCES += serializers/serializer.cpp serializers/xmlSerializer.cpp
SOURCES += serializers/jsonSerializer.cpp
SOURCES += serializers/xmlplistSerializer.cpp

INCLUDEPATH += ../libmythbase ../libmythservicecontracts ..
INCLUDEPATH += ./serializers
Expand Down
272 changes: 272 additions & 0 deletions mythtv/libs/libmythupnp/serializers/xmlplistSerializer.cpp
@@ -0,0 +1,272 @@
/* Class XmlPListSerializer
*
* Copyright (C) Mark Kendall 2012
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <QMetaClassInfo>
#include <QDateTime>

#include "xmlplistSerializer.h"

#define XMLPLIST_SERIALIZER_VERSION "1.0"

XmlPListSerializer::XmlPListSerializer(QIODevice *pDevice)
: XmlSerializer( pDevice, QString("") )
{
}

XmlPListSerializer::~XmlPListSerializer()
{
}

void XmlPListSerializer::BeginSerialize(QString &sName)
{
m_pXmlWriter->setAutoFormatting(true);
m_pXmlWriter->setAutoFormattingIndent(4);
m_pXmlWriter->writeStartDocument("1.0");
m_pXmlWriter->writeDTD("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
m_pXmlWriter->writeStartElement("plist");
m_pXmlWriter->writeAttribute("version", "1.0");
m_pXmlWriter->writeStartElement("dict"); // top level node
}

void XmlPListSerializer::EndSerialize(void)
{
m_pXmlWriter->writeEndElement(); // "dict"
m_pXmlWriter->writeEndElement(); // "plist"
m_pXmlWriter->writeEndDocument();
}

QString XmlPListSerializer::GetContentType()
{
return "text/x-apple-plist+xml";
}

void XmlPListSerializer::RenderValue(const QString &sName,
const QVariant &vValue,
bool needKey)
{
if ( vValue.canConvert<QObject*>())
{
const QObject *pObject = vValue.value<QObject*>();
SerializePListObjectProperties(pObject);
return;
}

switch(vValue.type())
{
case QVariant::List:
{
RenderList(sName, vValue.toList());
break;
}

case QVariant::StringList:
{
RenderStringList(sName, vValue.toStringList());
break;
}

case QVariant::Map:
{
RenderMap(sName, vValue.toMap());
break;
}

case QVariant::DateTime:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeTextElement("date", vValue.toDateTime()
.toUTC().toString(Qt::ISODate));
break;
}

case QVariant::ByteArray:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeTextElement("data",
vValue.toByteArray().toBase64().data());
break;
}

case QVariant::Bool:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeEmptyElement(vValue.toBool() ?
"true" : "false");
break;
}

case QVariant::UInt:
case QVariant::ULongLong:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeTextElement("integer",
QString::number(vValue.toULongLong()));
break;
}

case QVariant::Int:
case QVariant::LongLong:
case QVariant::Double:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeTextElement("real",
QString("%1").arg(vValue.toDouble(), 0, 'f', 6));
break;
}

// anything else will be unrecognised, so wrap in a string
case QVariant::String:
default:
{
if (needKey)
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeTextElement("string", vValue.toString());
break;
}
}
}

void XmlPListSerializer::RenderList(const QString &sName,
const QVariantList &list)
{
bool array = true;
if (!list.isEmpty())
{
QVariant::Type t = list[0].type();
QListIterator<QVariant> it(list);
while (it.hasNext())
{
if (it.next().type() != t)
{
array = false;
break;
}
}
}

QString sItemName = GetItemName(sName);
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeStartElement(array ? "array" : "dict");

QListIterator<QVariant> it(list);
while (it.hasNext())
RenderValue(sItemName, it.next(), !array);

m_pXmlWriter->writeEndElement();
}

void XmlPListSerializer::RenderStringList(const QString &sName,
const QStringList &list)
{
m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeStartElement("array");

QListIterator<QString> it(list);
while (it.hasNext())
m_pXmlWriter->writeTextElement("string", it.next());

m_pXmlWriter->writeEndElement();
}

void XmlPListSerializer::RenderMap(const QString &sName,
const QVariantMap &map)
{
QString sItemName = GetItemName(sName);
m_pXmlWriter->writeTextElement("key", sItemName);
m_pXmlWriter->writeStartElement("dict");

QMapIterator<QString,QVariant> it(map);
while (it.hasNext())
{
it.next();
RenderValue(it.key(), it.value());
}

m_pXmlWriter->writeEndElement();
}

void XmlPListSerializer::BeginObject(const QString &sName,
const QObject *pObject)
{
const QMetaObject *pMeta = pObject->metaObject();
int nIdx = pMeta->indexOfClassInfo("version");

if (nIdx >=0)
{
m_pXmlWriter->writeTextElement("key", "version");
m_pXmlWriter->writeTextElement("string", pMeta->classInfo(nIdx).value());
}

m_pXmlWriter->writeTextElement("key", "serializerversion");
m_pXmlWriter->writeTextElement("string", XMLPLIST_SERIALIZER_VERSION);

m_pXmlWriter->writeTextElement("key", sName);
m_pXmlWriter->writeStartElement("dict");
}

void XmlPListSerializer::EndObject(const QString &sName,
const QObject *pObject)
{
m_pXmlWriter->writeEndElement(); // dict
}

void XmlPListSerializer::AddProperty(const QString &sName,
const QVariant &vValue,
const QMetaObject *pMetaParent,
const QMetaProperty *pMetaProp)
{
RenderValue(sName, vValue);
}

void XmlPListSerializer::SerializePListObjectProperties(const QObject *pObject)
{
if (!pObject)
return;

m_pXmlWriter->writeStartElement("dict");

const QMetaObject *pMetaObject = pObject->metaObject();

int nCount = pMetaObject->propertyCount();

for (int nIdx=0; nIdx < nCount; ++nIdx)
{
QMetaProperty metaProperty = pMetaObject->property(nIdx);

if (metaProperty.isDesignable(pObject))
{
const char *pszPropName = metaProperty.name();
QString sPropName(pszPropName);

if (sPropName.compare("objectName") == 0)
continue;

QVariant value(pObject->property(pszPropName));

AddProperty(sPropName, value, pMetaObject, &metaProperty);
}
}

m_pXmlWriter->writeEndElement();
}
42 changes: 42 additions & 0 deletions mythtv/libs/libmythupnp/serializers/xmlplistSerializer.h
@@ -0,0 +1,42 @@
#ifndef XMLPLISTSERIALIZER_H
#define XMLPLISTSERIALIZER_H

#include <QXmlStreamWriter>
#include <QVariant>
#include <QIODevice>
#include <QStringList>

#include "upnpexp.h"
#include "xmlSerializer.h"

class UPNP_PUBLIC XmlPListSerializer : public XmlSerializer
{

protected:

virtual void BeginSerialize( QString &sName );
virtual void EndSerialize ();

void RenderValue ( const QString &sName, const QVariant &vValue , bool needKey = true);
void RenderStringList( const QString &sName, const QStringList &list );
void RenderList ( const QString &sName, const QVariantList &list );
void RenderMap ( const QString &sName, const QVariantMap &map );

virtual void BeginObject( const QString &sName, const QObject *pObject );
virtual void EndObject ( const QString &sName, const QObject *pObject );
virtual void AddProperty( const QString &sName,
const QVariant &vValue,
const QMetaObject *pMetaParent,
const QMetaProperty *pMetaProp );

void SerializePListObjectProperties( const QObject *pObject );

public:
XmlPListSerializer( QIODevice *pDevice );
virtual ~XmlPListSerializer();

virtual QString GetContentType();

};

#endif // XMLPLISTSERIALIZER_H

0 comments on commit 5a697eb

Please sign in to comment.