概述
JSON的全稱是”JavaScript Object Notation”,是一種基于文本,獨立于語言的輕量級資料交換格式。類似XML,也是一種資料交換格式,XML雖然可以作為跨平台的資料交換格式,XML标記比資料多,而JSON沒有附加的任何标記。
預設運作時,會創造一份新的遊戲檔案,運作效果如下:

可以通過指令行參數加載存檔檔案,運作效果如下(運作時需要在項目選項中設定指令行參數):
下面是本示例運作時所産生的save.json檔案(進行了一些名子的修改):
{
"levels": [
{
"name": "Village",
"npcs": [
{
"classType": 0,
"level": 8,
"name": "Barry the Blacksmith"
},
{
"classType": 0,
"level": 6,
"name": "Terry the Trader"
}
]
},
{
"name": "Dungeon",
"npcs": [
{
"classType": 1,
"level": 20,
"name": "Eric the Evil"
},
{
"classType": 0,
"level": 5,
"name": "Eric's Left Minion"
},
{
"classType": 0,
"level": 6,
"name": "張三"
}
]
}
],
"player": {
"classType": 2,
"level": 20,
"name": "阿西拜"
}
}
實作步驟
角色類
定義
#ifndef CHARACTER_H
#define CHARACTER_H
#include <QJsonObject>
#include <QObject>
#include <QString>
//遊戲中的角色
//可以是玩家的角色,也可以是非玩家的角色
class Character
{
//類似 Q_OBJECT,支援 QMetaObject的反射能力。但不支援信号槽機制。
//本例中要使用的Q_ENUM,需要這個宏的支援
Q_GADGET;
public:
enum ClassType {
Warrior, Mage, Archer
};
//Q_ENUM宏向元對象系統注冊枚舉類型
Q_ENUM(ClassType)
Character();
Character(const QString &name, int level, ClassType classType);
QString name() const;
void setName(const QString &name);
int level() const;
void setLevel(int level);
ClassType classType() const;
void setClassType(ClassType classType);
//我們研究的重點是read和write
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
void print(int indentation = 0) const;
private:
//需要維護的資料:名字,級别,職業
QString mName;
int mLevel;
ClassType mClassType;
};
#endif // CHARACTER_H
實作
#include "character.h"
#include <QMetaEnum>
#include <QTextStream>
Character::Character() :
mLevel(0),
mClassType(Warrior) {
}
Character::Character(const QString &name,
int level,
Character::ClassType classType) :
mName(name),
mLevel(level),
mClassType(classType)
{
}
QString Character::name() const
{
return mName;
}
void Character::setName(const QString &name)
{
mName = name;
}
int Character::level() const
{
return mLevel;
}
void Character::setLevel(int level)
{
mLevel = level;
}
Character::ClassType Character::classType() const
{
return mClassType;
}
void Character::setClassType(Character::ClassType classType)
{
mClassType = classType;
}
//! [讀取進度]
void Character::read(const QJsonObject &json)
{
if (json.contains("name") && json["name"].isString())
mName = json["name"].toString();
if (json.contains("level") && json["level"].isDouble())
mLevel = json["level"].toInt();
if (json.contains("classType") && json["classType"].isDouble())
mClassType = ClassType(json["classType"].toInt());
}
//! [讀取進度]
//! [寫進度]
void Character::write(QJsonObject &json) const
{
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
}
//! [寫進度]
void Character::print(int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
QTextStream(stdout) << indent << "Level:\t" << mLevel << "\n";
QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
QTextStream(stdout) << indent << "Class:\t" << className << "\n";
}
關卡等級類
類定義
#ifndef LEVEL_H
#define LEVEL_H
#include <QJsonObject>
#include <QVector>
#include "character.h"
//! [目前遊戲關卡級别]
//遊戲有很多的等級,不同的關卡級别非玩家角色也不同
class Level
{
public:
Level() = default;
Level(const QString &name);
QString name() const;
QVector<Character> npcs() const;
void setNpcs(const QVector<Character> &npcs);
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
void print(int indentation = 0) const;
private:
//需要維護的資料:本級别關卡的名稱,非玩家角色清單
QString mName;
QVector<Character> mNpcs;
};
//! [目前遊戲關卡級别]
#endif // LEVEL_H
類實作
#include "level.h"
#include <QJsonArray>
#include <QTextStream>
Level::Level(const QString &name) : mName(name)
{
}
QString Level::name() const
{
return mName;
}
QVector<Character> Level::npcs() const
{
return mNpcs;
}
void Level::setNpcs(const QVector<Character> &npcs)
{
mNpcs = npcs;
}
//![讀:從QJsonObject給mName和mNpcs指派]
void Level::read(const QJsonObject &json)
{
if (json.contains("name") && json["name"].isString())
mName = json["name"].toString();
//非玩家角色在json中以QJsonArray方式存在
if (json.contains("npcs") && json["npcs"].isArray()) {
QJsonArray npcArray = json["npcs"].toArray();
mNpcs.clear();
//提高效率,防止增加内容的時候重新配置設定記憶體空間
mNpcs.reserve(npcArray.size());
//周遊QJsonArray的内容,append到我們的成員變量中
for (int npcIndex = 0; npcIndex < npcArray.size(); ++npcIndex) {
QJsonObject npcObject = npcArray[npcIndex].toObject();
Character npc;
npc.read(npcObject);
mNpcs.append(npc);
}
}
}
//![讀:從QJsonObject給mName和mNpcs指派]
//![寫:将mName和mNpcs的值寫入QJsonObject]
void Level::write(QJsonObject &json) const
{
json["name"] = mName;
QJsonArray npcArray;
for (const Character &npc : mNpcs) {
QJsonObject npcObject;
npc.write(npcObject);
npcArray.append(npcObject);
}
json["npcs"] = npcArray;
}
//![寫:将mName和mNpcs的值寫入QJsonObject]
//!
void Level::print(int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
QTextStream(stdout) << indent << "NPCs:\n";
for (const Character &character : mNpcs)
character.print(2);
}
遊戲檔案類
類定義
#ifndef GAME_H
#define GAME_H
#include <QJsonObject>
#include <QVector>
#include "character.h"
#include "level.h"
//! [整個遊戲層面的存檔]
class Game
{
public:
enum SaveFormat {
Json, Binary
};
Character player() const;
QVector<Level> levels() const;
void newGame();
bool loadGame(SaveFormat saveFormat);
bool saveGame(SaveFormat saveFormat) const;
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
void print(int indentation = 0) const;
private:
//需要維護的資料:玩家資訊,目前遊戲關卡級别
Character mPlayer;
QVector<Level> mLevels;
};
//! [整個遊戲層面的存檔]
#endif // GAME_H
類實作
#include "game.h"
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QRandomGenerator>
#include <QTextStream>
Character Game::player() const
{
return mPlayer;
}
QVector<Level> Game::levels() const
{
return mLevels;
}
//! [建立遊戲是開啟新的存檔資訊]
void Game::newGame()
{
mPlayer = Character();
mPlayer.setName(QStringLiteral("Hero"));
mPlayer.setClassType(Character::Archer);
mPlayer.setLevel(QRandomGenerator::global()->bounded(15, 21));
mLevels.clear();
mLevels.reserve(2);
Level village(QStringLiteral("Village"));
QVector<Character> villageNpcs;
villageNpcs.reserve(2);
villageNpcs.append(Character(QStringLiteral("Barry the Blacksmith"),
QRandomGenerator::global()->bounded(8, 11),
Character::Warrior));
villageNpcs.append(Character(QStringLiteral("Terry the Trader"),
QRandomGenerator::global()->bounded(6, 8),
Character::Warrior));
village.setNpcs(villageNpcs);
mLevels.append(village);
Level dungeon(QStringLiteral("Dungeon"));
QVector<Character> dungeonNpcs;
dungeonNpcs.reserve(3);
dungeonNpcs.append(Character(QStringLiteral("Eric the Evil"),
QRandomGenerator::global()->bounded(18, 26),
Character::Mage));
dungeonNpcs.append(Character(QStringLiteral("Eric's Left Minion"),
QRandomGenerator::global()->bounded(5, 7),
Character::Warrior));
dungeonNpcs.append(Character(QStringLiteral("Eric's Right Minion"),
QRandomGenerator::global()->bounded(4, 9),
Character::Warrior));
dungeon.setNpcs(dungeonNpcs);
mLevels.append(dungeon);
}
//! [建立遊戲是開啟新的存檔資訊]
//! [加載存檔:調用read函數]
bool Game::loadGame(Game::SaveFormat saveFormat)
{
// enum SaveFormat {Json, Binary};
QFile loadFile(saveFormat == Json
? QStringLiteral("save.json")
: QStringLiteral("save.dat"));
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open save file.");
return false;
}
QByteArray saveData = loadFile.readAll();
QJsonDocument loadDoc(saveFormat == Json
? QJsonDocument::fromJson(saveData)
: QJsonDocument::fromBinaryData(saveData));
//loadDoc.object傳回文檔中包含的QJsonObject。
read(loadDoc.object());
QTextStream(stdout) << "Loaded save for "
<< loadDoc["player"]["name"].toString()
<< " using "
<< (saveFormat != Json ? "binary " : "") << "JSON...\n";
return true;
}
//! [加載存檔:調用read函數]
//! [存檔:調用write函數]
bool Game::saveGame(Game::SaveFormat saveFormat) const
{
QFile saveFile(saveFormat == Json
? QStringLiteral("save.json")
: QStringLiteral("save.dat"));
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning("Couldn't open save file.");
return false;
}
QJsonObject gameObject;
write(gameObject);
QJsonDocument saveDoc(gameObject);
saveFile.write(saveFormat == Json
? saveDoc.toJson()
: saveDoc.toBinaryData());
return true;
}
//! [存檔]
//! [讀:jason->mPlayer,jason->mLevels]
void Game::read(const QJsonObject &json)
{
if (json.contains("player") && json["player"].isObject())
mPlayer.read(json["player"].toObject());
if (json.contains("levels") && json["levels"].isArray()) {
QJsonArray levelArray = json["levels"].toArray();
mLevels.clear();
mLevels.reserve(levelArray.size());
for (int levelIndex = 0; levelIndex < levelArray.size(); ++levelIndex) {
QJsonObject levelObject = levelArray[levelIndex].toObject();
Level level;
level.read(levelObject);
mLevels.append(level);
}
}
}
//! [讀:jason->mPlayer,jason->mLevels]
//! [寫:mPlayer->jason,mLevels->jason]
void Game::write(QJsonObject &json) const
{
QJsonObject playerObject;
mPlayer.write(playerObject);
json["player"] = playerObject;
QJsonArray levelArray;
for (const Level &level : mLevels) {
QJsonObject levelObject;
level.write(levelObject);
levelArray.append(levelObject);
}
json["levels"] = levelArray;
}
//! [寫:mPlayer->jason,mLevels->jason]
void Game::print(int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Player\n";
mPlayer.print(indentation + 1);
QTextStream(stdout) << indent << "Levels\n";
for (Level level : mLevels)
level.print(indentation + 1);
}
mian函數
#include <QCoreApplication>
#include <QTextStream>
#include "game.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QStringList args = QCoreApplication::arguments();
bool newGame = true;
if (args.length() > 1)
newGame = (args[1].toLower() != QStringLiteral("load"));
bool json = true;
if (args.length() > 2)
json = (args[2].toLower() != QStringLiteral("binary"));
Game game;
if (newGame)
game.newGame();
else if (!game.loadGame(json ? Game::Json : Game::Binary))
return 1;
// Game is played; changes are made...
QTextStream(stdout) << "Game ended in the following state:\n";
game.print();
if (!game.saveGame(json ? Game::Json : Game::Binary))
return 1;
return 0;
}