- Game Programming Using Qt Beginner's Guide
- Witold Wysota Lorenz Haas
- 279字
- 2025-04-04 20:19:16
Time for action – the player data JSON serializer
Our next exercise is to create a serializer of the same PlayerInfo
structure as we used for the XML exercise, but this time the destination data format is going to be JSON.
Start by creating a PlayerInfoJSON
class and give it an interface similar to the one shown in the following code:
class PlayerInfoJSON { public: PlayerInfoJSON(){} QByteArray writePlayerInfo(const PlayerInfo &pinfo) const; };
All that is really required is to implement the writePlayerInfo
method. This method will use QJsonDocument::fromVariant()
to perform the serialization; thus, what we really have to do is convert our player data to a variant. Let's add a protected method to do that:
QVariant PlayerInfoJSON::toVariant(const PlayerInfo &pinfo) const { QVariantList players; foreach(const Player &p, pinfo.players) players << toVariant(p); return players; }
Since the structure is really a list of players, we can iterate the list of players, serialize each player to a variant, and append the result to QVariantList
. Having this function ready, we can descend a level and implement an overload for toVariant()
that takes a Player
object:
QVariant PlayerInfoJSON::toVariant(const Player &player) const { QVariantMap map; map["name"] = player.name; map["password"] = player.password; map["experience"] = player.experience; map["hitpoints"] = player.hitPoints; map["location"] = player.location; map["position"] = QVariantMap({ {"x", player.position.x()}, {"y", player.position.y()} }); map["inventory"] = toVariant(player.inventory); return map; }
Tip
Qt's foreach
macro takes two parameters—a declaration of a variable and a container to iterate. At each iteration, the macro assigns subsequent elements to the declared variable and executes the statement located directly after the macro. A C++11 equivalent of foreach
is a range that is based for construct:
for(const Player &p: pinfo.players) players << toVariant(p);
This time, we are using QVariantMap
as our base type, since we want to associate values with keys. For each key, we use the index operator to add entries to the map. The position key holds a QPoint
value, which is supported natively by QVariant
; however, such a variant can't be automatically encoded in JSON, so we convert the point to a variant map using the C++11 initializer list. The situation is different with the inventory—again, we have to write an overload for toVariant
that will perform the conversion:
QVariant PlayerInfoJSON::toVariant(const QList<InventoryItem> &items) const { QVariantList list; foreach(const InventoryItem &item, items) list << toVariant(item); return list; }
The code is almost identical to the one handling PlayerInfo
objects, so let's focus on the last overload of toVariant
—the one that accepts Item
instances:
QVariant PlayerInfoJSON::toVariant(const InventoryItem &item) const { QVariantMap map; map["type"] = (int)item.type; map["subtype"] = item.subType; map["durability"] = item.durability; return map; }
There is not much to comment here—we add all keys to the map, treating the item type as an integer for simplicity (this is not the best approach in a general case, as if we serialize our data and then change the order of values in the original enumeration, we will not get the proper item types after deserialization).
What remains is to use the code we have just written in the writePlayerInfo
method:
QByteArray PlayerInfoJSON::writePlayerInfo(const PlayerInfo &pinfo) const { QJsonDocument doc = QJsonDocument::fromVariant(toVariant(pinfo)); return doc.toJson(); }