Browse Source

Use JsonSerializeFormat for map objects

AlexVinS 9 years ago
parent
commit
dd1aabbe23

+ 21 - 13
lib/CCreatureSet.cpp

@@ -11,6 +11,7 @@
 #include "spells/CSpellHandler.h"
 #include "CHeroHandler.h"
 #include "IBonusTypeHandler.h"
+#include "serializer/JsonSerializeFormat.h"
 
 /*
  * CCreatureSet.cpp, part of VCMI engine
@@ -479,27 +480,34 @@ CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
 
 void CCreatureSet::armyChanged()
 {
+
 }
 
-void CCreatureSet::writeJson(JsonNode& json) const
+void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
 {
-	for(const auto & p : stacks)
+	if(handler.saving && stacks.empty())
+		return;
+	JsonNode & json = handler.getCurrent()[fieldName];
+
+	if(handler.saving)
 	{
-		JsonNode stack_node;
-		p.second->writeJson(stack_node);
-		json.Vector()[p.first.getNum()] = stack_node;
+		for(const auto & p : stacks)
+		{
+			JsonNode stack_node;
+			p.second->writeJson(stack_node);
+			json.Vector()[p.first.getNum()] = stack_node;
+		}
 	}
-}
-
-void CCreatureSet::readJson(const JsonNode& json)
-{
-	for(size_t idx = 0; idx < json.Vector().size(); idx++)
+	else
 	{
-		CStackInstance * new_stack = new CStackInstance();
+		for(size_t idx = 0; idx < json.Vector().size(); idx++)
+		{
+			CStackInstance * new_stack = new CStackInstance();
 
-		new_stack->readJson(json.Vector()[idx]);
+			new_stack->readJson(json.Vector()[idx]);
 
-		putStack(SlotID(idx), new_stack);
+			putStack(SlotID(idx), new_stack);
+		}
 	}
 }
 

+ 2 - 4
lib/CCreatureSet.h

@@ -19,6 +19,7 @@ class CCreature;
 class CGHeroInstance;
 class CArmedInstance;
 class CCreatureArtifactSet;
+class JsonSerializeFormat;
 
 class DLL_LINKAGE CStackBasicDescriptor
 {
@@ -220,10 +221,7 @@ public:
 		h & stacks & formation;
 	}
 
-
-	void writeJson(JsonNode & json) const;
-
-	void readJson(const JsonNode & json);
+	void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName);
 
 	operator bool() const
 	{

+ 0 - 10
lib/mapObjects/CArmedInstance.cpp

@@ -132,13 +132,3 @@ CBonusSystemNode * CArmedInstance::whatShouldBeAttached()
 {
 	return this;
 }
-
-void CArmedInstance::writeJsonOptions(JsonNode& json) const
-{
-	CGObjectInstance::writeJsonOptions(json);
-}
-
-void CArmedInstance::readJsonOptions(const JsonNode& json)
-{
-	CGObjectInstance::readJsonOptions(json);
-}

+ 0 - 3
lib/mapObjects/CArmedInstance.h

@@ -40,7 +40,4 @@ public:
 		h & static_cast<CBonusSystemNode&>(*this);
 		h & static_cast<CCreatureSet&>(*this);
 	}
-protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
 };

+ 30 - 24
lib/mapObjects/CGHeroInstance.cpp

@@ -25,6 +25,7 @@
 #include "../CTownHandler.h"
 #include "../mapping/CMap.h"
 #include "CGTownInstance.h"
+#include "../serializer/JsonSerializeFormat.h"
 
 ///helpers
 static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
@@ -1470,42 +1471,47 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subty
 	return (distance < visionsRange) && (target->pos.z == pos.z);
 }
 
-void CGHeroInstance::writeJsonOptions(JsonNode& json) const
+void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	if(type)
+	serializeJsonOwner(handler);
+
+	if(handler.saving)
 	{
-		json["type"].String() = type->identifier;
+		if(type)
+		{
+			handler.serializeString("type", type->identifier);
+		}
+		else
+		{
+			auto temp = VLC->heroh->heroes[subID]->identifier;
+			handler.serializeString("type", temp);
+		}
 	}
 	else
 	{
-		json["type"].String() = VLC->heroh->heroes[subID]->identifier;
-	}
-
-	CGObjectInstance::writeOwner(json);
+		if(ID == Obj::HERO || ID == Obj::PRISON)
+		{
+			std::string typeName;
+			handler.serializeString("type", typeName);
 
-	CCreatureSet::writeJson(json["army"]);
-	CArtifactSet::writeJson(json["artifacts"]);
+			auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", typeName);
 
-}
+			if(rawId)
+				subID = rawId.get();
+			else
+				subID = 0; //fallback to Orrin, throw error instead?
+		}
+	}
+	CCreatureSet::serializeJson(handler, "army");
 
-void CGHeroInstance::readJsonOptions(const JsonNode& json)
-{
-	if(ID == Obj::HERO || ID == Obj::PRISON)
 	{
-		auto typeName = json["type"].String();
-
-		auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", typeName);
-
-		if(rawId)
-			subID = rawId.get();
+		auto artifacts = handler.enterStruct("artifacts");
+		if(handler.saving)
+			CArtifactSet::writeJson(handler.getCurrent());
 		else
-			subID = 0; //fallback to Orrin, throw error instead?
+			CArtifactSet::readJson(handler.getCurrent());
 	}
 
-	CGObjectInstance::readOwner(json);
-
-	CCreatureSet::readJson(json["army"]);
-	CArtifactSet::readJson(json["artifacts"]);
 }
 
 bool CGHeroInstance::isMissionCritical() const

+ 1 - 2
lib/mapObjects/CGHeroInstance.h

@@ -251,8 +251,7 @@ public:
 	std::string getObjectName() const override;
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;//synchr
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
 private:
 	void levelUpAutomatically();

+ 12 - 10
lib/mapObjects/CGTownInstance.cpp

@@ -19,6 +19,7 @@
 #include "../CGameState.h"
 #include "../mapping/CMapDefines.h"
 #include "../CPlayerState.h"
+#include "../serializer/JsonSerializeFormat.h"
 
 std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
 std::vector<int> CGTownInstance::universitySkills;
@@ -314,18 +315,11 @@ void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
 	}
 }
 
-void CGDwelling::writeJsonOptions(JsonNode& json) const
+void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	//todo:CGDwelling::writeJsonOptions
+	//todo: CGDwelling::serializeJsonOptions
 	if(ID != Obj::WAR_MACHINE_FACTORY && ID != Obj::REFUGEE_CAMP)
-		CGObjectInstance::writeOwner(json);
-}
-
-void CGDwelling::readJsonOptions(const JsonNode& json)
-{
-	//todo:CGDwelling::readJsonOptions
-	if(ID != Obj::WAR_MACHINE_FACTORY && ID != Obj::REFUGEE_CAMP)
-		CGObjectInstance::readOwner(json);
+		serializeJsonOwner(handler);
 }
 
 int CGTownInstance::getSightRadius() const //returns sight distance
@@ -1129,6 +1123,14 @@ void CGTownInstance::battleFinished(const CGHeroInstance *hero, const BattleResu
 	}
 }
 
+void CGTownInstance::serializeJsonOptions(JsonSerializeFormat& handler)
+{
+	CGObjectInstance::serializeJsonOwner(handler);
+	CCreatureSet::serializeJson(handler, "army");
+
+	//todo: CGTownInstance::serializeJsonOptions
+}
+
 COPWBonus::COPWBonus (BuildingID index, CGTownInstance *TOWN)
 {
 	ID = index;

+ 2 - 2
lib/mapObjects/CGTownInstance.h

@@ -53,8 +53,7 @@ public:
 	TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
 
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
 private:
 	void initObj() override;
@@ -256,4 +255,5 @@ public:
 	std::string getObjectName() const override;
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };

+ 49 - 41
lib/mapObjects/CObjectHandler.cpp

@@ -24,6 +24,8 @@
 #include "CObjectClassesHandler.h"
 #include "CGTownInstance.h"
 
+#include "../serializer/JsonSerializeFormat.h"
+
 IGameCallback * IObjectInterface::cb = nullptr;
 
 ///helpers
@@ -331,64 +333,70 @@ bool CGObjectInstance::passableFor(PlayerColor color) const
 	return false;
 }
 
-void CGObjectInstance::writeJson(JsonNode & json) const
+void CGObjectInstance::serializeJson(JsonSerializeFormat & handler)
 {
-	logGlobal->debugStream() <<"Save: [" << pos << "] " << id << " " << ID << " " << subID << " " << typeName << " " << subTypeName;
+	if(handler.saving)
+	{
+		handler.serializeString("type", typeName);
+		handler.serializeString("subType", subTypeName);
+	}
 
-	json.setType(JsonNode::DATA_STRUCT);
-	json["type"].String() = typeName;
-	json["subType"].String() = subTypeName;
-	json["x"].Float() = pos.x;
-	json["y"].Float() = pos.y;
-	json["l"].Float() = pos.z;
+	handler.serializeNumeric("x", pos.x);
+	handler.serializeNumeric("y", pos.y);
+	handler.serializeNumeric("l", pos.z);
 
-	appearance.writeJson(json["template"], false);
-	writeJsonOptions(json["options"]);
-}
-
-void CGObjectInstance::readJson(const JsonNode & json)
-{
-	if(json.getType() != JsonNode::DATA_STRUCT)
+	if(handler.saving)
 	{
-		logGlobal->error("Invalid object instance data");
-		return;
+		appearance.writeJson(handler.getCurrent()["template"], false);
+	}
+	else
+	{
+		appearance.readJson(handler.getCurrent()["template"], false);
 	}
-	pos.x = json["x"].Float();
-	pos.y = json["y"].Float();
-	pos.z = json["l"].Float();
 
-	appearance.readJson(json["template"], false);
+	{
+		auto options = handler.enterStruct("options");
+		serializeJsonOptions(handler);
+	}
 
-	readJsonOptions(json["options"]);
+	if(handler.saving && handler.getCurrent()["options"].isNull())
+	{
+		handler.getCurrent().Struct().erase("options");
+	}
 }
 
-void CGObjectInstance::writeJsonOptions(JsonNode & json) const
+void CGObjectInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	json.setType(JsonNode::DATA_STRUCT);
-}
 
-void CGObjectInstance::readJsonOptions(const JsonNode & json)
-{
 }
 
-void CGObjectInstance::writeOwner(JsonNode & json) const
+void CGObjectInstance::serializeJsonOwner(JsonSerializeFormat & handler)
 {
-	if(tempOwner.isValidPlayer())
+	std::string temp;
+
+	//todo: use enum serialize
+	if(handler.saving)
 	{
-		json["owner"].String() = GameConstants::PLAYER_COLOR_NAMES[tempOwner.getNum()];
+		if(tempOwner.isValidPlayer())
+		{
+			temp = GameConstants::PLAYER_COLOR_NAMES[tempOwner.getNum()];
+			handler.serializeString("owner", temp);
+		}
 	}
-}
-
-void CGObjectInstance::readOwner(const JsonNode & json)
-{
-	tempOwner = PlayerColor::NEUTRAL;//this method assumes that object is ownable
-	if(json["owner"].getType() == JsonNode::DATA_STRING)
+	else
 	{
-		auto rawOwner = vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, json["owner"].String());
-		if(rawOwner >=0)
-			tempOwner = PlayerColor(rawOwner);
-		else
-			logGlobal->errorStream() << "Invalid owner :" << json["owner"].String();
+		tempOwner = PlayerColor::NEUTRAL;//this method assumes that object is ownable
+
+		handler.serializeString("owner", temp);
+
+		if(temp != "")
+		{
+			auto rawOwner = vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, temp);
+			if(rawOwner >=0)
+				tempOwner = PlayerColor(rawOwner);
+			else
+				logGlobal->errorStream() << "Invalid owner :" << temp;
+		}
 	}
 }
 

+ 5 - 12
lib/mapObjects/CObjectHandler.h

@@ -21,6 +21,7 @@ class IGameCallback;
 class CGObjectInstance;
 struct MetaString;
 struct BattleResult;
+class JsonSerializeFormat;
 
 // This one teleport-specific, but has to be available everywhere in callbacks and netpacks
 // For now it's will be there till teleports code refactored and moved into own file
@@ -186,10 +187,7 @@ public:
 	}
 
 	///Entry point of Json serialization
-	void writeJson(JsonNode & json) const;
-
-	///Entry point of Json de-serialization
-	void readJson(const JsonNode & json);
+	void serializeJson(JsonSerializeFormat & handler);
 
 protected:
 	/// virtual method that allows synchronously update object state on server and all clients
@@ -198,16 +196,11 @@ protected:
 	/// Gives dummy bonus from this object to hero. Can be used to track visited state
 	void giveDummyBonus(ObjectInstanceID heroID, ui8 duration = Bonus::ONE_DAY) const;
 
-	///Saves object-type specific options
-	///(!) do not forget to call inherited method first when overriding
-	virtual void writeJsonOptions(JsonNode & json) const;
+	///Serialize object-type specific options
+	virtual void serializeJsonOptions(JsonSerializeFormat & handler);
 
-	///Loads object-type specific options
-	///(!) do not forget to call inherited method  first when overriding
-	virtual void readJsonOptions(const JsonNode & json);
+	void serializeJsonOwner(JsonSerializeFormat & handler);
 
-	void writeOwner(JsonNode & json) const;
-	void readOwner(const JsonNode & json);
 private:
 	mutable std::string stringId;///<alternate id, dynamically generated, do not serialize
 };

+ 143 - 199
lib/mapObjects/MiscObjects.cpp

@@ -23,6 +23,7 @@
 #include "../CGameState.h"
 #include "../mapping/CMap.h"
 #include "../CPlayerState.h"
+#include "../serializer/JsonSerializeFormat.h"
 
 std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
 ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
@@ -593,59 +594,60 @@ void CGCreature::giveReward(const CGHeroInstance * h) const
 	}
 }
 
-static const std::string CHARACTER_JSON [] =
+static const std::vector<std::string> CHARACTER_JSON  =
 {
 	"compliant", "friendly", "aggressive", "hostile", "savage"
 };
 
-void CGCreature::writeJsonOptions(JsonNode& json) const
+void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	assert(vstd::iswithin(character, 0, 4));
-	json["character"].String() = CHARACTER_JSON[character];
+	handler.serializeNumericEnum("character", CHARACTER_JSON, (si8)0, character);
 
-	if(hasStackAtSlot(SlotID(0)))
+	if(handler.saving)
 	{
-		const auto & sta = getStack(SlotID(0));
-		json["amount"].Float() = sta.count;
-	}
+		if(hasStackAtSlot(SlotID(0)))
+		{
+			si32 amount = getStack(SlotID(0)).count;
+			handler.serializeNumeric("amount", amount);
+		}
 
-	json["noGrowing"].Bool() = notGrowingTeam;
-	json["neverFlees"].Bool() = neverFlees;
-	json["rewardMessage"].String() = message;
-	json["rewardArtifact"].String() = (gainedArtifact == ArtifactID(ArtifactID::NONE) ? "" : gainedArtifact.toArtifact()->identifier);
+		if(resources.nonZero())
+		{
+			for(size_t idx = 0; idx < resources.size(); idx++)
+				handler.getCurrent()["rewardResources"][GameConstants::RESOURCE_NAMES[idx]].Float() = resources[idx];
+		}
 
-	if(resources.nonZero())
-	{
-		for(size_t idx = 0; idx < resources.size(); idx++)
-			json["rewardResources"][GameConstants::RESOURCE_NAMES[idx]].Float() = resources[idx];
+		auto tmp = (gainedArtifact == ArtifactID(ArtifactID::NONE) ? "" : gainedArtifact.toArtifact()->identifier);
+		handler.serializeString("rewardArtifact", tmp);
 	}
-}
-
-void CGCreature::readJsonOptions(const JsonNode& json)
-{
-	character = vstd::find_pos(CHARACTER_JSON,json["character"].String());
-	vstd::amin(character, 0);
-
-	auto  hlp = new CStackInstance();
-	hlp->count = json["amount"].Float();
-	//type will be set during initialization
-	putStack(SlotID(0), hlp);
-
-	notGrowingTeam = json["noGrowing"].Bool();
-	neverFlees = json["neverFlees"].Bool();
-	message = json["rewardMessage"].String();
-
-	gainedArtifact = ArtifactID(ArtifactID::NONE);
-
-	if(json["rewardArtifact"].String() != "")
+	else
 	{
-		auto artid = VLC->modh->identifiers.getIdentifier("core", "artifact", json["rewardArtifact"].String());
-		if(artid)
-			gainedArtifact = ArtifactID(artid.get());
-	}
+		si32 amount = 0;
+		handler.serializeNumeric("amount", amount);
+		auto  hlp = new CStackInstance();
+		hlp->count = amount;
+		//type will be set during initialization
+		putStack(SlotID(0), hlp);
+		{
+			TResources tmp(handler.getCurrent()["rewardResources"]);
+			std::swap(tmp,resources);
+		}
+		{
+			gainedArtifact = ArtifactID(ArtifactID::NONE);
+			std::string tmp;
+			handler.serializeString("rewardArtifact", tmp);
 
-	TResources tmp(json["rewardResources"]);
-	std::swap(tmp,resources);
+			if(tmp != "")
+			{
+				auto artid = VLC->modh->identifiers.getIdentifier("core", "artifact", tmp);
+				if(artid)
+					gainedArtifact = ArtifactID(artid.get());
+			}
+		}
+	}
+	handler.serializeBool("noGrowing", notGrowingTeam);
+	handler.serializeBool("neverFlees", neverFlees);
+	handler.serializeString("rewardMessage", message);
 }
 
 //CGMine
@@ -790,71 +792,63 @@ void CGMine::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) con
 		cb->startBattleI(hero, this);
 }
 
-void CGMine::writeJsonOptions(JsonNode & json) const
+void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	CCreatureSet::writeJson(json["army"]);
+	CCreatureSet::serializeJson(handler, "army");
 
 	if(isAbandoned())
 	{
-		JsonNode & node = json["possibleResources"];
+		auto possibleResources = handler.enterStruct("possibleResources");
 
-		for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
-			if(tempOwner.getNum() & 1<<i)
-			{
-				JsonNode one(JsonNode::DATA_STRING);
-				one.String() = GameConstants::RESOURCE_NAMES[i];
-				node.Vector().push_back(one);
-			}
-	}
-	else
-	{
-		CGObjectInstance::writeOwner(json);
-	}
-}
-
-void CGMine::readJsonOptions(const JsonNode & json)
-{
-	CCreatureSet::readJson(json["army"]);
-
-	if(isAbandoned())
-	{
-		const JsonNode & node = json["possibleResources"];
-
-		std::set<int> possibleResources;
+		JsonNode & node = handler.getCurrent();
 
-		if(node.Vector().size() == 0)
+		if(handler.saving)
 		{
-			//assume all allowed
-			for(int i = (int)Res::WOOD; i < (int) Res::GOLD; i++)
-				possibleResources.insert(i);
+			for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
+				if(tempOwner.getNum() & 1<<i)
+				{
+					JsonNode one(JsonNode::DATA_STRING);
+					one.String() = GameConstants::RESOURCE_NAMES[i];
+					node.Vector().push_back(one);
+				}
 		}
 		else
 		{
-            auto names = node.convertTo<std::vector<std::string>>();
+			std::set<int> possibleResources;
 
-            for(const std::string & s : names)
+			if(node.Vector().size() == 0)
 			{
-                int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s);
-                if(raw_res < 0)
-					logGlobal->errorStream() << "Invalid resource name: "+s;
-				else
-					possibleResources.insert(raw_res);
+				//assume all allowed
+				for(int i = (int)Res::WOOD; i < (int) Res::GOLD; i++)
+					possibleResources.insert(i);
 			}
+			else
+			{
+				auto names = node.convertTo<std::vector<std::string>>();
 
-			int tmp = 0;
+				for(const std::string & s : names)
+				{
+					int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s);
+					if(raw_res < 0)
+						logGlobal->errorStream() << "Invalid resource name: "+s;
+					else
+						possibleResources.insert(raw_res);
+				}
 
-			for(int r : possibleResources)
-				tmp |=  (1<<r);
-			tempOwner = PlayerColor(tmp);
+				int tmp = 0;
+
+				for(int r : possibleResources)
+					tmp |=  (1<<r);
+				tempOwner = PlayerColor(tmp);
+			}
 		}
 	}
 	else
 	{
-		CGObjectInstance::readOwner(json);
+		serializeJsonOwner(handler);
 	}
 }
 
-
 std::string CGResource::getHoverText(PlayerColor player) const
 {
 	return VLC->generaltexth->restypes[subID];
@@ -939,18 +933,11 @@ void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
 		cb->startBattleI(hero, this);
 }
 
-void CGResource::writeJsonOptions(JsonNode& json) const
+void CGResource::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	CCreatureSet::writeJson(json["guards"]);
-	json["amount"].Float() = amount;
-	json["guardMessage"].String() = message;
-}
-
-void CGResource::readJsonOptions(const JsonNode& json)
-{
-	CCreatureSet::readJson(json["guards"]);
-	amount = json["amount"].Float();
-	message = json["guardMessage"].String();
+	CCreatureSet::serializeJson(handler, "guards");
+	handler.serializeNumeric("amount", amount);
+	handler.serializeString("guardMessage", message);
 }
 
 CGTeleport::CGTeleport() :
@@ -1439,25 +1426,21 @@ void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
 		cb->startBattleI(hero, this);
 }
 
-void CGArtifact::writeJsonOptions(JsonNode& json) const
+void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	CCreatureSet::writeJson(json["guards"]);
-	json["guardMessage"].String() = message;
-	if(ID == Obj::SPELL_SCROLL)
+	handler.serializeString("guardMessage", message);
+	CCreatureSet::serializeJson(handler, "guards");
+
+	if(handler.saving && ID == Obj::SPELL_SCROLL)
 	{
 		const Bonus * b = storedArtifact->getBonusLocalFirst(Selector::type(Bonus::SPELL));
 		SpellID spellId(b->subtype);
 
-		json["spell"].String() = SpellID(b->subtype).toSpell()->identifier;
+		std::string spell = SpellID(b->subtype).toSpell()->identifier;
+		handler.serializeString("spell", spell);
 	}
 }
 
-void CGArtifact::readJsonOptions(const JsonNode& json)
-{
-	CCreatureSet::readJson(json["guards"]);
-	message = json["guardMessage"].String();
-}
-
 void CGWitchHut::initObj()
 {
 	if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file
@@ -1515,17 +1498,11 @@ std::string CGWitchHut::getHoverText(const CGHeroInstance * hero) const
 	return hoverName;
 }
 
-void CGWitchHut::writeJsonOptions(JsonNode& json) const
+void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-
+	//todo:CGWitchHut::serializeJsonOptions
 }
 
-void CGWitchHut::readJsonOptions(const JsonNode& json)
-{
-
-}
-
-
 void CGMagicWell::onHeroVisit( const CGHeroInstance * h ) const
 {
 	int message;
@@ -1665,23 +1642,9 @@ std::string CGShrine::getHoverText(const CGHeroInstance * hero) const
 	return hoverName;
 }
 
-void CGShrine::writeJsonOptions(JsonNode& json) const
-{
-	if(spell != SpellID::NONE)
-	{
-		json["spell"].String() = spell.toSpell()->identifier;
-	}
-}
-
-void CGShrine::readJsonOptions(const JsonNode& json)
+void CGShrine::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	spell = SpellID::NONE;
-	if(json["spell"].String() != "")
-	{
-		auto raw = VLC->modh->identifiers.getIdentifier("core", "spell",json["spell"].String());
-		if(raw)
-			spell = SpellID(raw.get());
-	}
+	handler.serializeId("spell", &CSpellHandler::decodeSpell, &CSpellHandler::encodeSpell, SpellID(SpellID::NONE), spell);
 }
 
 void CGSignBottle::initObj()
@@ -1710,14 +1673,9 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
 		cb->removeObject(this);
 }
 
-void CGSignBottle::writeJsonOptions(JsonNode& json) const
-{
-	json["text"].String() = message;
-}
-
-void CGSignBottle::readJsonOptions(const JsonNode& json)
+void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	message = json["text"].String();
+	handler.serializeString("text", message);
 }
 
 void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
@@ -1793,52 +1751,55 @@ void CGScholar::initObj()
 	}
 }
 
-void CGScholar::writeJsonOptions(JsonNode& json) const
+void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
 {
-	switch(bonusType)
+	JsonNode& json = handler.getCurrent();
+	if(handler.saving)
 	{
-	case PRIM_SKILL:
-		json["rewardPrimSkill"].String() = PrimarySkill::names[bonusID];
-		break;
-	case SECONDARY_SKILL:
-		json["rewardSkill"].String() = NSecondarySkill::names[bonusID];
-		break;
-	case SPELL:
-		json["rewardSpell"].String() = VLC->spellh->objects.at(bonusID)->identifier;
-		break;
-	case RANDOM:
-		break;
-	}
-}
-
-void CGScholar::readJsonOptions(const JsonNode& json)
-{
-	bonusType = RANDOM;
-	if(json["rewardPrimSkill"].String() != "")
-	{
-		auto raw = VLC->modh->identifiers.getIdentifier("core", "primSkill", json["rewardPrimSkill"].String());
-		if(raw)
+		switch(bonusType)
 		{
-			bonusType = PRIM_SKILL;
-			bonusID = raw.get();
+		case PRIM_SKILL:
+			json["rewardPrimSkill"].String() = PrimarySkill::names[bonusID];
+			break;
+		case SECONDARY_SKILL:
+			json["rewardSkill"].String() = NSecondarySkill::names[bonusID];
+			break;
+		case SPELL:
+			json["rewardSpell"].String() = VLC->spellh->objects.at(bonusID)->identifier;
+			break;
+		case RANDOM:
+			break;
 		}
 	}
-	else if(json["rewardSkill"].String() != "")
+	else
 	{
-		auto raw = VLC->modh->identifiers.getIdentifier("core", "skill", json["rewardSkill"].String());
-		if(raw)
+		bonusType = RANDOM;
+		if(json["rewardPrimSkill"].String() != "")
 		{
-			bonusType = SECONDARY_SKILL;
-			bonusID = raw.get();
+			auto raw = VLC->modh->identifiers.getIdentifier("core", "primSkill", json["rewardPrimSkill"].String());
+			if(raw)
+			{
+				bonusType = PRIM_SKILL;
+				bonusID = raw.get();
+			}
 		}
-	}
-	else if(json["rewardSpell"].String() != "")
-	{
-		auto raw = VLC->modh->identifiers.getIdentifier("core", "spell", json["rewardSpell"].String());
-		if(raw)
+		else if(json["rewardSkill"].String() != "")
 		{
-			bonusType = SPELL;
-			bonusID = raw.get();
+			auto raw = VLC->modh->identifiers.getIdentifier("core", "skill", json["rewardSkill"].String());
+			if(raw)
+			{
+				bonusType = SECONDARY_SKILL;
+				bonusID = raw.get();
+			}
+		}
+		else if(json["rewardSpell"].String() != "")
+		{
+			auto raw = VLC->modh->identifiers.getIdentifier("core", "spell", json["rewardSpell"].String());
+			if(raw)
+			{
+				bonusType = SPELL;
+				bonusID = raw.get();
+			}
 		}
 	}
 }
@@ -1879,18 +1840,11 @@ void CGGarrison::battleFinished(const CGHeroInstance *hero, const BattleResult &
 		onHeroVisit(hero);
 }
 
-void CGGarrison::writeJsonOptions(JsonNode& json) const
-{
-	CCreatureSet::writeJson(json["army"]);
-	CGObjectInstance::writeOwner(json);
-	json["removableUnits"].Bool() = removableUnits;
-}
-
-void CGGarrison::readJsonOptions(const JsonNode& json)
+void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	CCreatureSet::readJson(json["army"]);
-	CGObjectInstance::readOwner(json);
-	removableUnits = json["removableUnits"].Bool();
+	handler.serializeBool("removableUnits", removableUnits);
+	serializeJsonOwner(handler);
+	CCreatureSet::serializeJson(handler, "army");
 }
 
 void CGMagi::initObj()
@@ -2030,14 +1984,9 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
 	}
 }
 
-void CGShipyard::writeJsonOptions(JsonNode& json) const
+void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	CGObjectInstance::writeOwner(json);
-}
-
-void CGShipyard::readJsonOptions(const JsonNode& json)
-{
-	CGObjectInstance::readOwner(json);
+	serializeJsonOwner(handler);
 }
 
 void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
@@ -2222,12 +2171,7 @@ void CGLighthouse::giveBonusTo( PlayerColor player ) const
 	cb->sendAndApply(&gb);
 }
 
-void CGLighthouse::writeJsonOptions(JsonNode& json) const
-{
-	CGObjectInstance::writeOwner(json);
-}
-
-void CGLighthouse::readJsonOptions(const JsonNode& json)
+void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)
 {
-	CGObjectInstance::readOwner(json);
+	serializeJsonOwner(handler);
 }

+ 11 - 26
lib/mapObjects/MiscObjects.h

@@ -86,11 +86,9 @@ public:
 	}
 protected:
 	void setPropertyDer(ui8 what, ui32 val) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
 private:
-
 	void fight(const CGHeroInstance *h) const;
 	void flee( const CGHeroInstance * h ) const;
 	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
@@ -101,7 +99,6 @@ private:
 
 };
 
-
 class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
 {
 public:
@@ -116,8 +113,7 @@ public:
 		h & message;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGWitchHut : public CPlayersVisited
@@ -136,8 +132,7 @@ public:
 		h & allowedAbilities & ability;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGScholar : public CGObjectInstance
@@ -156,8 +151,7 @@ public:
 		h & bonusType & bonusID;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGGarrison : public CArmedInstance
@@ -175,8 +169,7 @@ public:
 		h & removableUnits;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGArtifact : public CArmedInstance
@@ -202,8 +195,7 @@ public:
 		h & message & storedArtifact;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGResource : public CArmedInstance
@@ -227,8 +219,7 @@ public:
 		h & amount & message;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGShrine : public CPlayersVisited
@@ -246,8 +237,7 @@ public:
 		h & spell;
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGMine : public CArmedInstance
@@ -277,8 +267,7 @@ public:
 	}
 	ui32 defaultResProduction();
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 struct DLL_LINKAGE TeleportChannel
@@ -444,8 +433,7 @@ public:
 		h & static_cast<IShipyard&>(*this);
 	}
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
 
 class DLL_LINKAGE CGMagi : public CGObjectInstance
@@ -462,8 +450,6 @@ public:
 	}
 };
 
-
-
 class DLL_LINKAGE CCartographer : public CPlayersVisited
 {
 ///behaviour varies depending on surface and  floor
@@ -515,6 +501,5 @@ public:
 	}
 	void giveBonusTo( PlayerColor player ) const;
 protected:
-	void writeJsonOptions(JsonNode & json) const override;
-	void readJsonOptions(const JsonNode & json) override;
+	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };

+ 15 - 8
lib/mapping/MapFormatJson.cpp

@@ -525,7 +525,7 @@ std::unique_ptr<CMapHeader> CMapLoaderJson::loadMapHeader()
 	return std::move(result);
 }
 
-const JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename)
+JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename)
 {
 	ResourceID resource(archiveFilename, EResType::TEXT);
 
@@ -747,7 +747,7 @@ void CMapLoaderJson::readTerrain()
 
 }
 
-CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, const JsonMap::value_type& json):
+CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, JsonMap::value_type& json):
 	owner(_owner), instance(nullptr),id(-1), jsonKey(json.first), configuration(json.second), internalId(extractNumber(jsonKey, '_'))
 {
 
@@ -797,7 +797,9 @@ void CMapLoaderJson::MapObjectLoader::configure()
 	if(nullptr == instance)
 		return;
 
-	instance->readJson(configuration);
+	JsonDeserializer handler(configuration);
+
+	instance->serializeJson(handler);
 
 	if(instance->ID == Obj::TOWN)
 	{
@@ -841,10 +843,10 @@ void CMapLoaderJson::readObjects()
 
 	std::vector<std::unique_ptr<MapObjectLoader>> loaders;//todo: optimize MapObjectLoader memory layout
 
-	const JsonNode data = getFromArchive(OBJECTS_FILE_NAME);
+	JsonNode data = getFromArchive(OBJECTS_FILE_NAME);
 
 	//get raw data
-	for(const auto & p : data.Struct())
+	for(auto & p : data.Struct())
 		loaders.push_back(vstd::make_unique<MapObjectLoader>(this, p));
 
 	auto sortInfos = [](const std::unique_ptr<MapObjectLoader> & lhs, const std::unique_ptr<MapObjectLoader> & rhs) -> bool
@@ -993,8 +995,14 @@ void CMapSaverJson::writeObjects()
 {
 	JsonNode data(JsonNode::DATA_STRUCT);
 
-	for(const CGObjectInstance * obj : map->objects)
-		obj->writeJson(data[obj->getStringId()]);
+	JsonSerializer handler(data);
+
+	for(CGObjectInstance * obj : map->objects)
+	{
+		auto temp = handler.enterStruct(obj->getStringId());
+
+		obj->serializeJson(handler);
+	}
 
 	if(map->grailPos.valid())
 	{
@@ -1010,7 +1018,6 @@ void CMapSaverJson::writeObjects()
 		std::string grailId = boost::str(boost::format("grail_%d") % map->objects.size());
 
 		data[grailId] = grail;
-
 	}
 
 	addToArchive(data, OBJECTS_FILE_NAME);

+ 3 - 3
lib/mapping/MapFormatJson.h

@@ -159,12 +159,12 @@ private:
 
 	struct MapObjectLoader
 	{
-		MapObjectLoader(CMapLoaderJson * _owner, const JsonMap::value_type & json);
+		MapObjectLoader(CMapLoaderJson * _owner, JsonMap::value_type & json);
 		CMapLoaderJson * owner;
 		CGObjectInstance * instance;
 		ObjectInstanceID id;
 		std::string jsonKey;//full id defined by map creator
-		const JsonNode & configuration;
+		JsonNode & configuration;
 		si32 internalId;//unique part of id defined by map creator (also = quest identifier)
 		///constructs object (without configuration)
 		void construct();
@@ -197,7 +197,7 @@ private:
 	 */
 	void readObjects();
 
-	const JsonNode getFromArchive(const std::string & archiveFilename);
+	JsonNode getFromArchive(const std::string & archiveFilename);
 
 	CInputStream * buffer;
 	std::shared_ptr<CIOApi> ioApi;

+ 18 - 0
lib/serializer/JsonDeserializer.cpp

@@ -48,6 +48,24 @@ void JsonDeserializer::serializeIntEnum(const std::string & fieldName, const std
 		value = rawValue;
 }
 
+void JsonDeserializer::serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value)
+{
+	std::string identifier;
+	serializeString(fieldName, identifier);
+
+	if(identifier == "")
+	{
+		value = defaultValue;
+		return;
+	}
+
+	si32 rawId = decoder(identifier);
+	if(rawId >= 0)
+		value = rawId;
+	else
+		value = defaultValue;
+}
+
 void JsonDeserializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
 {
 	const JsonNode & field = current->operator[](fieldName);

+ 1 - 0
lib/serializer/JsonDeserializer.h

@@ -27,4 +27,5 @@ public:
 protected:
 	void serializeFloat(const std::string & fieldName, double & value) override;
 	void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
+	void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) override;
 };

+ 12 - 0
lib/serializer/JsonSerializeFormat.h

@@ -98,6 +98,16 @@ public:
 
 	virtual void serializeString(const std::string & fieldName, std::string & value) = 0;
 
+	template <typename T>
+	void serializeId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const T & defaultValue, T & value)
+	{
+		const si32 tempDefault = defaultValue.num;
+		si32 tempValue = value.num;
+		serializeIntId(fieldName, decoder, encoder, tempDefault, tempValue);
+		if(!saving)
+			value = T(tempValue);
+	}
+
 protected:
 	JsonNode * root;
 	JsonNode * current;
@@ -107,6 +117,8 @@ protected:
 	virtual void serializeFloat(const std::string & fieldName, double & value) = 0;
 
 	virtual void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) = 0;
+
+	virtual void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) = 0;
 private:
 	friend class JsonStructSerializer;
 };

+ 9 - 0
lib/serializer/JsonSerializer.cpp

@@ -40,6 +40,15 @@ void JsonSerializer::serializeIntEnum(const std::string & fieldName, const std::
 	current->operator[](fieldName).String() = enumMap.at(value);
 }
 
+void JsonSerializer::serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32& value)
+{
+	if(defaultValue == value)
+		return;
+
+	std::string identifier = encoder(value);
+	serializeString(fieldName, identifier);
+}
+
 void JsonSerializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
 {
 	assert(standard.size() == value.size());

+ 1 - 0
lib/serializer/JsonSerializer.h

@@ -27,4 +27,5 @@ public:
 protected:
 	void serializeFloat(const std::string & fieldName, double & value) override;
 	void serializeIntEnum(const std::string & fieldName, const std::vector<std::string> & enumMap, const si32 defaultValue, si32 & value) override;
+	void serializeIntId(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const si32 defaultValue, si32 & value) override;
 };