this uses operator overloading and ctor overload to convert formats. it seems reasonable.
struct Custom { Custom() { } Custom(const Custom & _src) = default; // note; you can knuckle down here and insist on specialisation of QJV and only // allow a QJA. that would make the caller double check, not a bad thing // see note below about QJsonContainer Custom(const QJsonValue & _) { QJsonArray ja = _.toArray(); if (ja != QJsonArray()) { for (int i = 0; i < 4; i++) { special_data[i] = ja.at(i).toInt(0); } } } // it's not symmetrical at all operator QJsonValue() const { return QJsonArray{ special_data[0], special_data[1], special_data[2], special_data[3]}; } // output this as javascript… yes… output this…… as javascript……… union { float normal_data; char special_data[4]; unsigned short empty_data[2]; }; };
most other implementations (game read/write 4xmpl) implement read/write functions all the way down the object tree. this approach seems interesting as chaining (treeing) the output reads very nicely. see below. placing the JSON read in the ctor means a default value is available if anything inside fails nad error can be returned with a simple error state variable
Custom custom; custom.normal_data = 3.141592653f; QJsonObject jo; jo.insert("_data_version", "1"); jo.insert("_data", custom);
// make some data up Custom custom_array[3]; custom_array[0].normal_data = 1.0; custom_array[1].normal_data = -1.0; custom_array[2].normal_data = 0.0; // use QJsonArray initializer lists QJsonArray ja { custom_array[0], custom_array[1], custom_array[2] };
these utility functions convert a file to/from JSON format using Qt
QJsonObject and QJsonArray are accepted/returned by the QJsonDocument de/serializer. cowever, should they both derive from a common “QJsonContainer” then no distinction would need to be made concluding this would be that QJsonContainer is readily converted to a QJsonValue. That is a QJsonValue can take a QJsonContainer (it can already take QJsonObject and QJsonArray)
they convert to a QJsonValue rather than an object or array. failure is indicated by a null value. first see comments about a QJsonContainer class above then return here. 'fromFile' indicates failure by returning the JSON 'null' value. this value is not valid at the top level of a file, however, the toFile function also accepts a QJsonValue meaning null can be pass in. toFile should only accept a QJsonContainer
QJsonValue fromFile(const QString & _path) { QFile file(_path); if (file.open(QIODevice::ReadOnly)) { // interesting, can't cast up // that is QJsonDocument -> QJsonValue is impossible // however, i bet there is a single json value inside the doc QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); // what's the top level? just want to return a value (spec is nothing here) // what's the problem with assiging a json doc to a json value? if (doc.isArray()) { return QJsonValue(doc.array()); } else if (doc.isObject()) { return QJsonValue(doc.object()); } else if (doc.isNull()) { return QJsonValue(); } else if (doc.isEmpty()) { return QJsonValue(); } } return QJsonValue(); }
bool toFile(const QJsonValue & _src, const QString & _path) { QFile file(_path); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { // again with the conversion, maybe i should care, i don't QJsonDocument doc; if (_src.isArray()) { doc.setArray(_src.toArray()); file.write(doc.toJson()); return true; } else if (_src.isObject()) { doc.setObject(_src.toObject()); file.write(doc.toJson()); return true; } } return false; }