Tests working for reading JSON.

This commit is contained in:
Andrzej Kapolka 2014-06-16 19:12:21 -07:00
parent 77b6a209ab
commit 37c977af02
4 changed files with 260 additions and 41 deletions

View file

@ -1178,7 +1178,9 @@ Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) {
}
for (int i = 0; i < localProperties.size(); i++) {
const StreamerPropertyPair& property = properties.at(i);
if (localProperties.at(i).first != property.first || property.second.propertyIndex() != i) {
const StreamerPropertyPair& localProperty = localProperties.at(i);
if (property.first != localProperty.first ||
property.second.propertyIndex() != localProperty.second.propertyIndex()) {
streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties));
return *this;
}
@ -1866,7 +1868,6 @@ void JSONWriter::addSharedObject(const SharedObjectPointer& object) {
QJsonObject sharedObject;
sharedObject.insert("id", object->getID());
sharedObject.insert("originID", object->getOriginID());
sharedObject.insert("data", getData(static_cast<const QObject*>(object.data())));
_sharedObjects.replace(index, sharedObject);
}
@ -1887,17 +1888,121 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge
for (int i = types.size() - 1; i >= 0; i--) {
QJsonObject type = types.at(i).toObject();
QString name = type.value("name").toString();
QByteArray latinName = name.toLatin1();
const TypeStreamer* baseStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(latinName));
if (!baseStreamer) {
baseStreamer = Bitstream::getEnumStreamersByName().value(latinName);
}
if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) {
continue;
}
QString category = type.value("category").toString();
if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) {
TypeStreamerPointer streamer;
if (category == "ENUM") {
QVector<NameIntPair> values;
int highestValue = 0;
foreach (const QJsonValue& value, type.value("values").toArray()) {
QJsonObject object = value.toObject();
int value = object.value("value").toInt();
highestValue = qMax(value, highestValue);
values.append(NameIntPair(object.value("key").toString().toLatin1(), value));
}
streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(latinName,
values, getBitsForHighestValue(highestValue), QByteArray()));
} else if (category == "STREAMABLE") {
QVector<StreamerNamePair> fields;
foreach (const QJsonValue& field, type.value("fields").toArray()) {
QJsonObject object = field.toObject();
fields.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()),
object.value("name").toString().toLatin1()));
}
streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(latinName,
fields, QByteArray()));
} else if (category == "LIST") {
streamer = TypeStreamerPointer(new GenericListTypeStreamer(latinName,
getTypeStreamer(type.value("valueType").toString())));
} else if (category == "SET") {
streamer = TypeStreamerPointer(new GenericSetTypeStreamer(latinName,
getTypeStreamer(type.value("valueType").toString())));
} else if (category == "MAP") {
streamer = TypeStreamerPointer(new GenericMapTypeStreamer(latinName,
getTypeStreamer(type.value("keyType").toString()),
getTypeStreamer(type.value("valueType").toString())));
}
_typeStreamers.insert(name, streamer);
static_cast<GenericTypeStreamer*>(streamer.data())->_weakSelf = streamer;
continue;
}
if (category == "ENUM") {
QHash<int, int> mappings;
int highestValue = 0;
QMetaEnum metaEnum = baseStreamer->getMetaEnum();
QJsonArray array = type.value("values").toArray();
bool matches = (array.size() == metaEnum.keyCount());
foreach (const QJsonValue& value, array) {
QJsonObject object = value.toObject();
int value = object.value("value").toInt();
highestValue = qMax(value, highestValue);
int mapping = metaEnum.keyToValue(object.value("key").toString().toLatin1());
if (mapping != -1) {
mappings.insert(value, mapping);
}
matches &= (value == mapping);
}
if (matches) {
_typeStreamers.insert(name, baseStreamer->getSelf());
} else {
_typeStreamers.insert(name, TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer,
getBitsForHighestValue(highestValue), mappings)));
}
} else if (category == "STREAMABLE") {
QVector<StreamerIndexPair> fields;
QJsonArray array = type.value("fields").toArray();
const QVector<MetaField>& metaFields = baseStreamer->getMetaFields();
bool matches = (array.size() == metaFields.size());
for (int j = 0; j < array.size(); j++) {
QJsonObject object = array.at(j).toObject();
TypeStreamerPointer streamer = getTypeStreamer(object.value("type").toString());
int index = baseStreamer->getFieldIndex(object.value("name").toString().toLatin1());
fields.append(StreamerIndexPair(streamer, index));
matches &= (index == j && streamer == metaFields.at(j).getStreamer());
}
if (matches) {
_typeStreamers.insert(name, baseStreamer->getSelf());
} else {
_typeStreamers.insert(name, TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)));
}
} else if (category == "LIST") {
TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString());
if (valueStreamer == baseStreamer->getValueStreamer()) {
_typeStreamers.insert(name, baseStreamer->getSelf());
} else {
_typeStreamers.insert(name, TypeStreamerPointer(new MappedListTypeStreamer(baseStreamer, valueStreamer)));
}
} else if (category == "SET") {
TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString());
if (valueStreamer == baseStreamer->getValueStreamer()) {
_typeStreamers.insert(name, baseStreamer->getSelf());
} else {
_typeStreamers.insert(name, TypeStreamerPointer(new MappedSetTypeStreamer(baseStreamer, valueStreamer)));
}
} else if (category == "MAP") {
TypeStreamerPointer keyStreamer = getTypeStreamer(type.value("keyType").toString());
TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString());
if (keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer()) {
_typeStreamers.insert(name, baseStreamer->getSelf());
} else {
_typeStreamers.insert(name, TypeStreamerPointer(new MappedMapTypeStreamer(
baseStreamer, keyStreamer, valueStreamer)));
}
}
}
@ -1905,11 +2010,45 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge
for (int i = classes.size() - 1; i >= 0; i--) {
QJsonObject clazz = classes.at(i).toObject();
QString name = clazz.value("name").toString();
QJsonArray properties = clazz.value("properties").toArray();
foreach (const QJsonValue& property, properties) {
QJsonObject object = property.toObject();
object.value("type");
object.value("name");
QByteArray latinName = name.toLatin1();
const ObjectStreamer* baseStreamer = Bitstream::getObjectStreamers().value(
Bitstream::getMetaObjects().value(latinName));
if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) {
continue;
}
if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) {
QVector<StreamerNamePair> properties;
foreach (const QJsonValue& property, clazz.value("properties").toArray()) {
QJsonObject object = property.toObject();
properties.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()),
object.value("name").toString().toLatin1()));
}
ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer(
latinName, properties, QByteArray()));
_objectStreamers.insert(name, streamer);
static_cast<GenericObjectStreamer*>(streamer.data())->_weakSelf = streamer;
continue;
}
const QMetaObject* metaObject = baseStreamer->getMetaObject();
const QVector<StreamerPropertyPair>& baseProperties = baseStreamer->getProperties();
QVector<StreamerPropertyPair> properties;
QJsonArray propertyArray = clazz.value("properties").toArray();
bool matches = (baseProperties.size() == propertyArray.size());
for (int j = 0; j < propertyArray.size(); j++) {
QJsonObject object = propertyArray.at(j).toObject();
TypeStreamerPointer typeStreamer = getTypeStreamer(object.value("type").toString());
QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(
object.value("name").toString().toLatin1()));
properties.append(StreamerPropertyPair(typeStreamer, metaProperty));
const StreamerPropertyPair& baseProperty = baseProperties.at(i);
matches &= (typeStreamer == baseProperty.first &&
metaProperty.propertyIndex() == baseProperty.second.propertyIndex());
}
if (matches) {
_objectStreamers.insert(name, baseStreamer->getSelf());
} else {
_objectStreamers.insert(name, ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)));
}
}
@ -1917,9 +2056,11 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge
for (int i = objects.size() - 1; i >= 0; i--) {
QJsonObject object = objects.at(i).toObject();
int id = object.value("id").toInt();
int originID = object.value("originID").toInt();
QJsonObject data = object.value("data").toObject();
QObject* qObject;
putData(object.value("data"), qObject);
if (qObject) {
_sharedObjects.insert(id, static_cast<SharedObject*>(qObject));
}
}
_contents = top.value("contents").toArray();
@ -2051,16 +2192,12 @@ void JSONReader::putData(const QJsonValue& data, const QMetaObject*& value) {
void JSONReader::putData(const QJsonValue& data, QVariant& value) {
QJsonObject object = data.toObject();
QString type = object.value("type").toString();
const TypeStreamer* streamer = _typeStreamers.value(type).data();
if (!streamer) {
streamer = Bitstream::getTypeStreamers().value(QMetaType::type(type.toLatin1()));
if (!streamer) {
qWarning() << "Unknown type:" << type;
value = QVariant();
return;
}
TypeStreamerPointer streamer = getTypeStreamer(type);
if (streamer) {
streamer->putJSONVariantData(*this, object.value("value"), value);
} else {
value = QVariant();
}
streamer->putJSONData(*this, object.value("value"), value);
}
void JSONReader::putData(const QJsonValue& data, SharedObjectPointer& value) {
@ -2073,6 +2210,19 @@ void JSONReader::putData(const QJsonValue& data, QObject*& value) {
value = streamer ? streamer->putJSONData(*this, object) : NULL;
}
TypeStreamerPointer JSONReader::getTypeStreamer(const QString& name) const {
TypeStreamerPointer streamer = _typeStreamers.value(name);
if (!streamer) {
const TypeStreamer* defaultStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(name.toLatin1()));
if (defaultStreamer) {
streamer = defaultStreamer->getSelf();
} else {
qWarning() << "Unknown type:" << name;
}
}
return streamer;
}
ObjectStreamer::ObjectStreamer(const QMetaObject* metaObject) :
_metaObject(metaObject) {
}
@ -2438,6 +2588,10 @@ void TypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVari
value = QVariant();
}
void TypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const {
putJSONData(reader, data, value);
}
bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const {
return first == second;
}
@ -2750,6 +2904,12 @@ const char* GenericTypeStreamer::getName() const {
return _name.constData();
}
void GenericTypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const {
QVariant containedValue;
putJSONData(reader, data, containedValue);
value = QVariant::fromValue(GenericValue(_weakSelf, containedValue));
}
QVariant GenericTypeStreamer::readVariant(Bitstream& in) const {
return QVariant::fromValue(GenericValue(_weakSelf, read(in)));
}

View file

@ -893,7 +893,7 @@ public:
template<class T> JSONReader& operator>>(T& value) { putData(*_contentsIterator++, value); return *this; }
TypeStreamerPointer getTypeStreamer(const QString& name) const { return _typeStreamers.value(name); }
TypeStreamerPointer getTypeStreamer(const QString& name) const;
ObjectStreamerPointer getObjectStreamer(const QString& name) const { return _objectStreamers.value(name); }
SharedObjectPointer getSharedObject(int id) const { return _sharedObjects.value(id); }
@ -1020,6 +1020,7 @@ public:
private:
friend class Bitstream;
friend class JSONReader;
QByteArray _name;
WeakObjectStreamerPointer _weakSelf;
@ -1107,6 +1108,7 @@ public:
virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const;
virtual QJsonValue getJSONVariantData(JSONWriter& writer, const QVariant& value) const;
virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const;
virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const;
virtual bool equal(const QVariant& first, const QVariant& second) const;
@ -1237,11 +1239,13 @@ public:
GenericTypeStreamer(const QByteArray& name);
virtual const char* getName() const;
virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const;
virtual QVariant readVariant(Bitstream& in) const;
protected:
friend class Bitstream;
friend class JSONReader;
QByteArray _name;
WeakTypeStreamerPointer _weakSelf;

View file

@ -252,8 +252,64 @@ static bool testSerialization(Bitstream::MetadataType metadataType) {
jsonWriter << testObjectReadA;
jsonWriter << testObjectReadB;
jsonWriter << messageRead;
qDebug() << jsonWriter.getDocument().toJson();
qDebug();
jsonWriter << endRead;
QByteArray encodedJson = jsonWriter.getDocument().toJson();
// and read from JSON
JSONReader jsonReader(QJsonDocument::fromJson(encodedJson), Bitstream::ALL_GENERICS);
jsonReader >> testObjectReadA;
jsonReader >> testObjectReadB;
jsonReader >> messageRead;
jsonReader >> endRead;
// reassign the ids
testObjectReadA->setID(testObjectWrittenA->getID());
testObjectReadA->setOriginID(testObjectWrittenA->getOriginID());
testObjectReadB->setID(testObjectWrittenB->getID());
testObjectReadB->setOriginID(testObjectWrittenB->getOriginID());
// and back to binary
QByteArray secondCompareArray;
QDataStream secondCompareOutStream(&secondCompareArray, QIODevice::WriteOnly);
Bitstream secondCompareOut(secondCompareOutStream, Bitstream::FULL_METADATA);
secondCompareOut << testObjectReadA;
secondCompareOut << testObjectReadB;
secondCompareOut << messageRead;
secondCompareOut << endRead;
secondCompareOut.flush();
if (compareArray != secondCompareArray) {
qDebug() << "Mismatch between written/JSON streams (generics).";
return true;
}
// once more, with mapping!
JSONReader secondJSONReader(QJsonDocument::fromJson(encodedJson));
secondJSONReader >> testObjectReadA;
secondJSONReader >> testObjectReadB;
secondJSONReader >> messageRead;
secondJSONReader >> endRead;
// reassign the ids
testObjectReadA->setID(testObjectWrittenA->getID());
testObjectReadA->setOriginID(testObjectWrittenA->getOriginID());
testObjectReadB->setID(testObjectWrittenB->getID());
testObjectReadB->setOriginID(testObjectWrittenB->getOriginID());
// and back to binary
QByteArray thirdCompareArray;
QDataStream thirdCompareOutStream(&thirdCompareArray, QIODevice::WriteOnly);
Bitstream thirdCompareOut(thirdCompareOutStream, Bitstream::FULL_METADATA);
thirdCompareOut << testObjectReadA;
thirdCompareOut << testObjectReadB;
thirdCompareOut << messageRead;
thirdCompareOut << endRead;
thirdCompareOut.flush();
if (compareArray != thirdCompareArray) {
qDebug() << "Mismatch between written/JSON streams (mapped).";
return true;
}
return false;
}

View file

@ -123,11 +123,6 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
out << "QHash<QByteArray, int> " << name << "::createFieldIndices() {\n";
out << " QHash<QByteArray, int> indices;\n";
out << " int index = 1;\n";
foreach (const QString& base, str.clazz.bases) {
out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n";
out << " indices.insert(field.getName(), index++);\n";
out << " }\n";
}
out << " foreach (const MetaField& field, getMetaFields()) {\n";
out << " indices.insert(field.getName(), index++);\n";
out << " }\n";
@ -231,15 +226,19 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
out << "}\n";
out << "template<> void JSONReader::putData(const QJsonValue& data, " << name << "& value) {\n";
out << " QJsonArray array = data.toArray();\n";
out << " QJsonArray::const_iterator it = array.constBegin();\n";
/* foreach (const QString& base, str.clazz.bases) {
out << " foreach (const QJsonValue& element, getData(static_cast<const " << base << "&>(value)).toArray()) {\n";
out << " array.append(element);\n";
out << " }\n";
} */
foreach (const Field& field, str.fields) {
out << " putData(*it++, value." << field.name << ");\n";
if (!(str.clazz.bases.isEmpty() && str.fields.isEmpty())) {
out << " QJsonArray array = data.toArray(), subarray;\n";
out << " QJsonArray::const_iterator it = array.constBegin();\n";
foreach (const QString& base, str.clazz.bases) {
out << " subarray = QJsonArray();\n";
out << " for (int i = 0; i < " << base << "::getMetaFields().size(); i++) {\n";
out << " subarray.append(*it++);\n";
out << " }\n";
out << " putData(subarray, static_cast<" << base << "&>(value));\n";
}
foreach (const Field& field, str.fields) {
out << " putData(*it++, value." << field.name << ");\n";
}
}
out << "}\n";