Unsolved QObject::setProperty and custom class
-
I have written a custom class for holding links information:
class Link { public: .... bool operator != ( const Link& _left ) { return false; return ( m_sHref == _left.m_sHref ) && ( m_sRel == _left.m_sRel ); } private: QString m_sHref; QString m_sRel; }; Q_DECLARE_METATYPE( Link )
and I am writing a class for enterprise entities to hold data with ability to populate itself from JSON and XML:
class EnterpriseEntity : public QObject { public: Q_OBJECT public: EnterpriseEntity( QObject* _parent = nullptr ) : QObject( _parent ) {} EnterpriseEntity& fromJson( const QByteArray& _json_doc ) { QJsonDocument doc = QJsonDocument::fromJson( _json_doc ); QJsonObject obj = doc.object(); for ( QString var : obj.keys() ) { if ( this->setProperty( var.toStdString().c_str(), obj[var].toVariant() ) == false ) { qDebug() << "Error in seting: " << var; } } return *this; } }; // EnterpriseEntity
and my json object is like this:
{"family":"Kafman","id":18,"link":{"href":"http://localhost/api/users/Ema","rel":"self"},"name":"Ema"}
finally I have created an User entity like this:
class User : public EnterpriseEntity { public: Q_OBJECT Q_PROPERTY(QString name MEMBER m_sName ) Q_PROPERTY(QString family MEMBER m_sFamily ) Q_PROPERTY(QString id MEMBER m_iID ) Q_PROPERTY(Link link MEMBER m_sLink ) public: .... private: QString m_iID; QString m_sName; QString m_sFamily; Link m_sLink; }
the problem is that while setProperty in EnterpriseEntity::fromJson works fine with QString, it returns false for "link". It has been for two days that I am struggling to tackle this issue yet I haven't succeeded. I would be grateful if you could help me to overcome this difficultly.
-
link is an object, as documented here it will be converted to a QVariantMap, having href and rel as keys. There is no way for the
QJasonValue::toVariant()
to know that your object is aLink
so it cannot possibly convert it to it.if you provide:
Link::Link(const QVariantMap& val){ m_sHref = val.value("href").toString(); m_sRel = val.value("rel").toString(); } Link& Link::operator=(const QVariantMap& val){ m_sHref = val.value("href").toString(); m_sRel = val.value("rel").toString(); return *this; }
it might work
-
Actually, I have provided, but It did not work
Link ( const QVariantMap& _map ) { m_sHref = _map["href"].toString(); m_sRel = _map["rel"].toString(); } Link& operator= ( const QVariantMap& _map ) { m_sHref = _map["href"].toString(); m_sRel = _map["rel"].toString(); qDebug() << "called" ; return *this; }
-
could you
qDebug() << obj[var].type() << " - " << obj[var].toVariant().type();
and tell us what it prints?I'm afraid you'll have to do it manually btw
-
I have added following code to 'EnterpriseEntity::fromJson' method (in the loop):
qDebug() << this->metaObject()->className() << var << obj[var].toVariant(); qDebug() << obj[var].type() << " - " << obj[var].toVariant().type();
and output is like that:
User "link" QVariant(QVariantMap, QMap(("href", QVariant(QString, "http://localhost/api/users/Ema"))("rel", QVariant(QString, "self")))) 5 - QVariant::QVariantMap
-
Then I'm afraid it's manual:
const auto allKeys = obj.keys(); for ( const QString& var : allKeys ) //this is more efficient { if(obj[var].type()==QJsonValue::Object){ if ( !setProperty( var.toLatin1().constData(), obj[var].type()==QJsonValue::Object ? QVariant(Link(obj[var].toVariant())): obj[var].toVariant() ) ) { qDebug() << "Error in seting: " << var; } }
-
@VRonin said in QObject::setProperty and custom class:
Then I'm afraid it's manual:
I hope not. It would be the worst option. In particular for big and enterprise software.
-
ok, last try (as I think I'm getting drunk on all these nested types)
Link::Link(const QVariant& vart){ if(vart.type()==QMetaType::QVariantMap){ const QVariantMap val= vart.toVariantMap(); m_sHref = val.value("href").toString(); m_sRel = val.value("rel").toString(); } } Link& Link::operator=(const QVariant& vart){ if(vart.type()==QMetaType::QVariantMap){ const QVariantMap val= vart.toVariantMap(); m_sHref = val.value("href").toString(); m_sRel = val.value("rel").toString(); } return *this; }
-
@VRonin said in QObject::setProperty and custom class:
ok, last try (as I think I'm getting drunk on all these nested types)
I had done that too :(.
Telling the truth, me too. however Qt is great library, it doesn't support some basic and essential classes ( and I haven't any idea why?! ).For instance, there is not any class for working with integers that could accept null values, while there are database adapters and support in it ( and it goes without saying that nullable fields are a vital part of any database design.)
I was about writing these classes by myself (such as Integer, etc.), nevertheless I faced to this problem.
this is a complete version of my Link class:
class Link { public: explicit Link ( ); Link ( const Link& _link ); Link ( const QVariant& _var ); Link ( const QVariantMap& _map ); operator QVariant() const; bool operator != ( const Link& _left ); Link& operator= ( const QVariantMap& _map ); Link& operator= ( const QVariant& _var ); QString getHref( void ) const { return m_sHref; } void setHref( const QString& _link ) { m_sHref = _link; } QString getRelation( void) const { return m_sRel; } void setRelation( const QString& _rel ) { m_sRel = _rel; } private: QString m_sHref; QString m_sRel; };
-
could you try to implement an explicit reader and writer for the function? it might be that in that case implicit conversion is executed (so keep that QVariant constructor):It is manual :(
@Pswin said in QObject::setProperty and custom class:
it doesn't support some basic and essential classes
Let's be honest, this is far from essential, you are just trying to find a lazy solution that saves you checking what
var
contains. an if on it solves your problem straight awayP.S.
@Pswin said in QObject::setProperty and custom class:integers that could accept null values
That's actually very easy to implement using pointers (or smart pointers):
std::unique_ptr<int> nullableInt; //start with a null int if(nullableInt) qDebug() << "int is valid, value: " << *nullableInt; else qDebug("int is null"); nullableInt = std::make_unique<int>(3); // set it to 3 if(nullableInt) qDebug() << "int is valid, value: " << *nullableInt; else qDebug("int is null"); nullableInt.reset(); //set it back to null if(nullableInt) qDebug() << "int is valid, value: " << *nullableInt; else qDebug("int is null");
-
Using 'Boost.Optional' would be better option:
http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html
like:
boost::optional<int> m_iID;
Honestly, As I am developing a large scale application, I am looking for ways that are not error prone. Checking members manually again and again in every class makes software more liable for bugs and harder to control and revise. As result, such class are essential to me (at least).
-
@Pswin said in QObject::setProperty and custom class:
For instance, there is not any class for working with integers that could accept null values
This makes no sense at all. There's no
null
semantics put into integers, that's a db thing and C++ has no notion of it. As such the ambiguity is wrapped inQVariant
(which wraps around aunion
) as it should.QVariant::isNull
is probably what you're expecting to find.@VRonin said in QObject::setProperty and custom class:
That's actually very easy to implement using pointers (or smart pointers)
Oh, come on Luca, are we going to start creating individual characters in the heap next?
Here's how it could be done the right way (whichQVariant
already does internally):struct SpecialValue { enum SupportedTypes { NullType, IntType, DoubleType }; SupportedTypes type; // Contains the type currently set (the active field in the union) union { int intValue; double doubleValue; } data; // The data };
What you probably want to implement is an interface that will abstract the streaming from and to a
JsonObject
and all objects that implement that would call each of their properties that implement it to serialize themselves. Then the other way around is exactly the same. E.g. (I leave the serialization to your imagination):class JsonSerializable { public: virtual QJsonObject toJsonObject() const = 0; virtual void fromJsonObject(const QJsonObject &) = 0; }; class EnterpriseEntity : public QObject, public JsonSerializable { // ... void fromJsonObject(const QJsonObject & object) override { // Other properties // ... // Link Link link; link.fromJsonObject(object.value("link").toObject()); setProperty("link", link); } } class Link : public JsonSerializable { public: void fromJsonObject(const QJsonObject & object) override { m_sHref = object.value("href").toString(); m_sRel = object.value("rel").toString(); } };