JsonWax for Qt

NOW WITH SERIALIZATION

------ JsonWax JsonWax JsonWax --- JsonWax JsonWax JsonWax ------------ JsonWax JsonWax JsonWax ---- JsonWax JsonWax JsonWax ------------- JsonWax JsonWax JsonWax ---------- JsonWax JsonWax JsonWax ----------- JsonWax JsonWax JsonWax -------- JsonWax JsonWax JsonWax ------------ JsonWax JsonWax JsonWax ------- JsonWax JsonWax JsonWax JsonWax JsonWax JsonWax --------- JsonWax JsonWax JsonWax ----------- JsonWax JsonWax JsonWax -- JsonWax JsonWax JsonWax -------------- JsonWax JsonWax JsonWax ---------- JsonWax JsonWax JsonWax -------- JsonWax JsonWax JsonWax ---- JsonWax JsonWax JsonWax --------- JsonWax JsonWax JsonWax ---- JsonWax JsonWax JsonWax -- JsonWax JsonWax JsonWax ---------- JsonWax JsonWax JsonWax --------- JsonWax JsonWax JsonWax ------------- JsonWax JsonWax JsonWax - JsonWax JsonWax JsonWax ---------- JsonWax JsonWax JsonWax ----------- JsonWax JsonWax JsonWax ------------ JsonWax JsonWax JsonWax ---------- JsonWax JsonWax JsonWax -------------- JsonWax JsonWax JsonWax ------- JsonWax JsonWax JsonWax JsonWax JsonWax JsonWax -------- JsonWax JsonWax JsonWax ----- JsonWax JsonWax JsonWax JsonWax JsonWax JsonWax

FUNCTIONALITY

Here is all that you can do in JsonWax!

int append(const QVariantList& keys, const QVariant& value)

Appends a value to the end of the JSON-array found at the location. If there's no array at the location, the location's data is removed, and an array is created in its place, containing value.

void copy(const QVariantList& keysFrom, const QVariantList& keysTo)

Copies the data at a location, to another location. This function cannot enter a recursive loop, since the data is first copied to a temporary location, before it overwrites the data at the destination.

void copy(const QVariantList& keysFrom, JsonWax& jsonTo, const QVariantList& keysTo)

Copies the data at a location in this JsonWax-object, to a location in another JsonWax-object.

T deserializeBytes(const QVariantList& keys, const T defaultValue = T())

Deserialize the Base64-encoded object located at keys. Use like this:

QColor color = deserializeBytes<QColor>({0,"serializedColor"});

See also serializeToBytes().

void deserializeBytes(T& outputHere, const QVariantList& keys)

Deserialize the Base64-encoded object located at keys. The data will be stored in the provided variable - this is useful if you are outputting to a QObject, since it doesn't have a default copy constructor (and you don't need to write <Type>).
Use like this:

QColor color;
qDebug() << "old color:" << color;
json.deserializeBytes( color, {0,"serializedColor"});
qDebug() << "new color:" << color;

See also serializeToBytes().

T deserializeJson(const QVariantList& keys, T defaultValue = T())

Deserialize the 'serialized-to-JSON' object located at keys.
Use like this:

QDateTime datetime = json.deserializeJson<QDateTime>({0, "date1});

See also serializeToJson().

void deserializeJson(T& outputHere, const QVariantList& keys)

Deserialize the 'serialized-to-JSON' object located at keys, and store it in the provided variable. Like mentioned in the second deserializeBytes()-function, this can be useful for deserializing QObjects, since they don't have copy constructors.

QDateTime datetime;
qDebug() << "old:" << datetime;
json.deserializeJson( datetime, {0, "date1"});
qDebug() << "new:" << datetime;

See also serializeToJson().

int errorCode()

Returns the integer code for the last parsing (0 means that it was OK).

QString errorMsg()

Returns the error message for the latest errorCode.

int errorPos()

Returns the position of the last parser error (character number). (I'm not sure that the position is completely accurate)
Returns -1 if the last parsing was without error.

bool exists(const QVariantList& keys)

Returns true if the location exists, and false if it doesn't. exists({}) always returns true, since {} is the root element.

JsonWax json;
json.fromByteArray("{\"A\":{\"B\":\"cow\"}}");
qDebug() << json.exists({"A"});
qDebug() << json.exists({"A","B"});
qDebug() << json.exists({"A","B","cow"});

It returns:

true
true
false

bool fromByteArray(const QByteArray& bytes)

Loads a JSON-document from a QByteArray. To load a document from a QString do this:

JsonWax json;
json.fromByteArray( jsonString.toUtf8());

The function returns true if the string is a valid JSON-document, and false if it isn't. See also loadFile().

bool isArray( const QVariantList& keys)

Returns true if the location is an Array, and false if it isn't.

bool isNullValue( const QVariantList& keys)

Returns true if the location is a value with value "null", and false if it's anything else.

bool isObject( const QVariantList& keys)

Returns true if the location is an Object, and false if it isn't.

bool isValue( const QVariantList& keys)

Returns true if the location is a Value, and false if it isn't.

QVariantList keys(const QVariantList& keys)

Returns a list of all keys found at the location. If the location is an array, the returned QList contains ints, which are the existing array positions. If the location is a map, the command returns the keys in that map.

bool loadFile(const QString& fileName)

Loads a JSON-document from file. If the function returns true, the file existed, and was a valid JSON-document. If it returns false, the file didn't exist or the document was invalid. loadFile() can be used when constructing an object:

JsonWax json ("jsonFile.json");

If you use the above, you can't know whether the document was valid or invalid. Instead you could do:

JsonWax json;
if (json.loadFile("jsonFile.json"))
    qDebug() << "valid document";
else
    qDebug() << "invalid document";

It is tested whether the input is a relative or an absolute path, and the file is loaded if it exists. So you can do both:

JsonWax json;
json.loadFile("subfolder/jsonFile.json");

and

JsonWax json;
json.loadFile("c:/jsonFile.json");

void move(const QVariantList& keysFrom, const QVariantList& keysTo)

Moves data from one location to another. This function is very efficient, since it just moves a pointer from A to B.

void move(const QVariantList& keysFrom, JsonWax& jsonTo, const QVariantList& keysTo)

Moves data from one location in this JsonWax-object, to a location in another JsonWax-document.

void popFirst(const QVariantList& keys, int removeTimes = 1)

Removes the first value from the array found at the location, decreasing its size by one. If it's not an array nothing will happen.

void popLast(const QVariantList& keys, int removeTimes = 1)

Removes the last value from the array found at the location, decreasing its size by one. If it's not an array nothing will happen.

void prepend(const QVariantList& keys, const QVariant& value)

Prepends a value to the array found at the location. If it's not an array, the data at the location is removed, and an array is created.

void remove(const QVariantList& keys)

Removes what exists at a location, and all of its sub-keys and values. If the location doesn't exist, nothing happens. An empty keys-list removes everything. If the last key is an int, it removes an element from the QList, reducing its size by 1.
If array positions are important to your data structure (and you insist on using ints in your keys-list), instead of using remove(), you could use setNull(keys), which would keep your list at the same size. An alternative to this situation is to only use strings to reference locations within the document.

bool save(StringStyle style = Readable, bool convertToCodePoints = false)

Saves the document to the file which it was loaded from. If the document wasn't loaded from a file, an error will be returned, and the document won't be saved. Like saveAs() it returns true if all bytes were written to the file, and false if they weren't. See also saveAs().

bool saveAs(const QString& fileName, StringStyle style = Readable, bool convertToCodePoints = false, bool overwriteAllowed = true)

saveAs() converts the document to a QString, and writes the document to a file. If you've loaded the document from a file using loadFile(), you can use save() instead, to overwrite the loaded document, without specifying a name.
Keys within a JSON-object are always output in alphabetical order. Lists are always kept in the same order. style can be either JsonWax::Compact or JsonWax::Readable, and determines whether the output will be a JSON-document without spaces and newlines, or if it will be nicely formatted.
convertToCodePoints converts unicode characters to the \uXXXX notation.

void serializeToBytes( const QVariantList& keys, const T& object)

Serialize the given object as a Base64-encoded byte array. It becomes an unreadable string which can be deserialized back to an object. It would be much more efficient to write and read an object as a regular byte array, and the data is unreadable, which is why many would probably call this function an abomination. I recommend that you generally use serializeToJson(), since the object then becomes readable, editable strings. However, serializeToBytes is much faster than serializeToJson. serializeToBytes can be very useful to store fx. small pixmaps. It also works for QObjects.

Don't store large amounts of data with this function. Instead, do something more efficient.

void serializeToJson( const QVariantList& keys, const T& object)

Serialize the given object as a JSON-Object or string, and store it at the specified location.

It's currently working for these data types:
QColor, QDate, QDateTime, QLine, QLineF, QList, QMap, QObject, QPoint, QPointF, QRect, QRectF, QSize, QTime, QUrl, QVariant.

The QMap must be JSON-compatible, so it is required to have QString as the key: QMap<QString,T>.
The QLists and QMaps may be arbitrarily nested.
Use deserializeJson() to deserialize back to an object.

Here's an example where a QDateTime object is serialized to JSON, and then immediately deserialized.

QDateTime datetime = QDateTime::currentDateTimeUtc();
JsonWax json;
json.serializeToJson({0}, datetime);
datetime = json.deserializeJson({0});
qDebug() << datetime;
json.saveAs("SerializerTest.json");

The above code outputs the current UTC date and time to console:

QDateTime(2017-05-26 15:59:05.131 UTC Qt::TimeSpec(UTC))

And here are the contents of SerializerTest.json:

[
  {
    "date": "2017-05-26",
    "time": "15:59:05.131",
    "timeSpec": "1"
  }
]

That's it! With one line of code you can serialize a number of Qt data types directly to JSON.

Make your class inherit from QObject, in order to make it serializable (to a limit).

class SerializerClass1 : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QString name MEMBER m_name)
  Q_PROPERTY(int superNumber MEMBER m_number)
  Q_PROPERTY(Enume1 walker MEMBER m_enume1)
  Q_PROPERTY(QDate bestDate MEMBER m_date)
  Q_PROPERTY(QColor coolColor MEMBER m_color)
  Q_PROPERTY(QImage imageAttempt MEMBER m_image) // Can't save its data.
  Q_ENUMS(Enume1) // Required to store the enum.
public:
  SerializerClass1( QObject* parent = 0): QObject(parent){}
  enum Enume1 {Crab, Spider};
private:
  QString m_name = "One\nWonderful Name";
  int m_number = 1900000;
  Enume1 m_enume1 = Crab;
  QDate m_date = QDate(1950,10,10);
  QColor m_color = QColor(15,16,16,255);
  QImage m_image = QImage(512,512,QImage::Format_Mono);
};

All of the variables specified using Q_PROPERTY(T name MEMBER variable_name) will be saved. The serialization in a QObject is independent from non-QObject JsonWax serialization, which explains the differences in supported data types and in the serialized data.
If, for instance, JsonWax serializes a QTime object directly, it will include ms, whereas in QObject serialization it will only include hh:mm:ss.
See below how to serialize and deserialize a SerializerClass1 object:

SerializerClass1 object;
JsonWax json;
json.serializeToJson( {}, object);
json.deserializeJson( object, {});
json.saveAs("SerializerTest.json");

The contents of "SerializerTest.json" are:

{
  "bestDate": "1950-10-10",
  "coolColor": "#0f1010",
  "imageAttempt": "",
  "name": "One\nWonderful Name",
  "objectName": "",
  "superNumber": "1900000",
  "walker": "0"
}

The "objectName" is part of the QObject. Please don't store a variable under that name. Or under the name of any other variable.
Even though QObject supports calling Properties the same name, JsonWax serialization doesn't support that.

void setEmptyArray( const QVariantList& keys)

Turns the location into an empty array.

void setEmptyObject( const QVariantList& keys)

Turns the location into an empty object.

void setNull( const QVariantList& keys)

Sets the value of the location to null.

JsonWax json;
json.setValue({"A", "B"}, "this");
json.setValue({"A", "C"}, "that");
qDebug() << "1:" << json.toString(JsonWax::Compact);
json.setNull({"A"});
qDebug() << "2:" << json.toString(JsonWax::Compact);

This returns:

1: "{\"A\":{\"B\":\"this\",\"C\":\"that\"}}"
2: "{\"A\":null}"

You can check whether a value is null by:

JsonWax json;
json.setValue({"A", "X"}, "this");
json.setNull({"A"});
qDebug() << json.value({"A"}).isNull();
qDebug() << json.value({"B"}).isNull();
true
true

Notice that the existing null, and the non-existing value are both null.

void setValue(const QVariantList& keys, const QVariant& value)

Stores the value at the keys-location. This overwrites anything that already exists at that location, or creates the value.
Example:

JsonWax json;
json.setValue({"this","is","my","place"}, 1565);
json.setValue({"this","is","my","place"}, 12.535);
json.setValue({"this","is","my","document"}, "friendship");
json.setValue({"this","is","my","area"}, false);
json.setValue({"this","is","my","life"}, QVariant());
json.setValue({"this","is","my","area"}, true;

which gives the JSON-document:

{
  "this": {
    "is": {
      "my": {
        "area": true,
        "document": "friendship",
        "life": null,
        "place": 12.535
      }
    }
  }
}

See also setNull().

int size(const QVariantList& keys = {})

Returns the number of keys found at the location, no matter if it's an Object or an Array. Returns 1 if the location is a JsonValue, and returns -1 if the location doesn't exist. Use size({}) or just size() to get the size of the root Object or Array.

JsonWax json;
json.setValue({"A", "B"}, "this");
json.setValue({"C", "D"}, "thing");
json.setValue({"E", 0}, "is");
json.setValue({"E", 1}, "a");
json.setValue({"E", 2}, "thing");
json.setValue({"E", 3}, ".");
qDebug() << "object:" << json.size({});
qDebug() << "array:" << json.size({"E"});

Which outputs:

object: 3
array: 4

QString toString(StringStyle style = Readable, bool convertToCodePoints = false, const QVariantList& keys = {})

This function converts the data in the JsonWax-object into a QString. If keys isn't changed from its default, the function outputs the entire document as string. With keys you can choose to stringify just part of the document.
If you attempt to turn a non-existing location, or a value, into a string, this function returns "{}". Therefore, the result is always a valid JSON-document.
See also save() and saveAs().

Type type(const QVariantList& keys)

Returns the type of the location. The Type can be either: Value, Object, Array, Null.
An entry with value "null" is considered a Value; it's only considered Null if it doesn't exist.

QVariant value(const QVariantList& keys, const QVariant& defaultValue = QVariant())

Returns the QVariant found by following the sequence of keys. You can check if a value is null by using isNull() on the returned value. isNull() returns true both when a value is null in the JSON-document, and when it doesn't exist at all.
You can set a default value, which will be returned if the location isn't a value, or the keys-list is empty.
When you use a number as a key, remember that 0 is the first element in the Array.

JsonWax json;
json.setValue({"alpha","beta","gamma","delta"}, "ding");
json.setValue({"alpha","beta","gamma","echo", 4}, "location");
qDebug() << json.value({"alpha","beta","gamma","delta"}).toString();
qDebug() << json.value({"alpha","beta","gamma","echo",4}).toString();
qDebug() << json.value({"trout"}, "no trout").toString();
json.saveAs("output.json");

qDebug() outputs:

"ding"
"location"
"no trout"

And "output.json" looks like this:

{
  "alpha": {
    "beta": {
      "gamma": {
        "delta": "ding",
        "echo": [
          null,
          null,
          null,
          null,
          "location"
        ]
      }
    }
  }
}