/* * JsonNode.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "GameConstants.h" #include "filesystem/ResourcePath.h" VCMI_LIB_NAMESPACE_BEGIN class JsonNode; using JsonMap = std::map; using JsonVector = std::vector; struct Bonus; class CSelector; class CAddInfo; class ILimiter; class DLL_LINKAGE JsonNode { public: enum class JsonType { DATA_NULL, DATA_BOOL, DATA_FLOAT, DATA_STRING, DATA_VECTOR, DATA_STRUCT, DATA_INTEGER }; private: using JsonData = std::variant; JsonData data; public: /// free to use metadata fields std::string meta; // meta-flags like override std::vector flags; //Create empty node JsonNode(JsonType Type = JsonType::DATA_NULL); //Create tree from Json-formatted input explicit JsonNode(const char * data, size_t datasize); //Create tree from JSON file explicit JsonNode(const JsonPath & fileURI); explicit JsonNode(const std::string & modName, const JsonPath & fileURI); explicit JsonNode(const JsonPath & fileURI, bool & isValidSyntax); bool operator == (const JsonNode &other) const; bool operator != (const JsonNode &other) const; void setMeta(const std::string & metadata, bool recursive = true); /// Convert node to another type. Converting to nullptr will clear all data void setType(JsonType Type); JsonType getType() const; bool isNull() const; bool isNumber() const; bool isString() const; bool isVector() const; bool isStruct() const; /// true if node contains not-null data that cannot be extended via merging /// used for generating common base node from multiple nodes (e.g. bonuses) bool containsBaseData() const; bool isCompact() const; /// removes all data from node and sets type to null void clear(); /// returns bool or bool equivalent of string value if 'success' is true, or false otherwise bool TryBoolFromString(bool & success) const; /// non-const accessors, node will change type on type mismatch bool & Bool(); double & Float(); si64 & Integer(); std::string & String(); JsonVector & Vector(); JsonMap & Struct(); /// const accessors, will cause assertion failure on type mismatch bool Bool() const; ///float and integer allowed double Float() const; ///only integer allowed si64 Integer() const; const std::string & String() const; const JsonVector & Vector() const; const JsonMap & Struct() const; /// returns resolved "json pointer" (string in format "/path/to/node") const JsonNode & resolvePointer(const std::string & jsonPointer) const; JsonNode & resolvePointer(const std::string & jsonPointer); /// convert json tree into specified type. Json tree must have same type as Type /// Valid types: bool, string, any numeric, map and vector /// example: convertTo< std::map< std::vector > >(); template Type convertTo() const; //operator [], for structs only - get child node by name JsonNode & operator[](const std::string & child); const JsonNode & operator[](const std::string & child) const; JsonNode & operator[](size_t child); const JsonNode & operator[](size_t child) const; std::string toJson(bool compact = false) const; template void serialize(Handler &h, const int version) { h & meta; h & flags; h & data; } }; namespace JsonUtils { DLL_LINKAGE std::shared_ptr parseBonus(const JsonVector & ability_vec); DLL_LINKAGE std::shared_ptr parseBonus(const JsonNode & ability); DLL_LINKAGE std::shared_ptr parseBuildingBonus(const JsonNode & ability, const FactionID & faction, const BuildingID & building, const std::string & description); DLL_LINKAGE bool parseBonus(const JsonNode & ability, Bonus * placement); DLL_LINKAGE std::shared_ptr parseLimiter(const JsonNode & limiter); DLL_LINKAGE CSelector parseSelector(const JsonNode &ability); DLL_LINKAGE void resolveAddInfo(CAddInfo & var, const JsonNode & node); /** * @brief recursively merges source into dest, replacing identical fields * struct : recursively calls this function * arrays : each entry will be merged recursively * values : value in source will replace value in dest * null : if value in source is present but set to null it will delete entry in dest * @note this function will destroy data in source */ DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source, bool ignoreOverride = false, bool copyMeta = false); /** * @brief recursively merges source into dest, replacing identical fields * struct : recursively calls this function * arrays : each entry will be merged recursively * values : value in source will replace value in dest * null : if value in source is present but set to null it will delete entry in dest * @note this function will preserve data stored in source by creating copy */ DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source, bool ignoreOverride = false, bool copyMeta = false); /** @brief recursively merges descendant into copy of base node * Result emulates inheritance semantic */ DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base); /** * @brief construct node representing the common structure of input nodes * @param pruneEmpty - omit common properties whose intersection is empty * different types: null * struct: recursive intersect on common properties * other: input if equal, null otherwise */ DLL_LINKAGE JsonNode intersect(const JsonNode & a, const JsonNode & b, bool pruneEmpty = true); DLL_LINKAGE JsonNode intersect(const std::vector & nodes, bool pruneEmpty = true); /** * @brief construct node representing the difference "node - base" * merging difference with base gives node */ DLL_LINKAGE JsonNode difference(const JsonNode & node, const JsonNode & base); /** * @brief generate one Json structure from multiple files * @param files - list of filenames with parts of json structure */ DLL_LINKAGE JsonNode assembleFromFiles(const std::vector & files); DLL_LINKAGE JsonNode assembleFromFiles(const std::vector & files, bool & isValid); /// This version loads all files with same name (overridden by mods) DLL_LINKAGE JsonNode assembleFromFiles(const std::string & filename); /** * @brief removes all nodes that are identical to default entry in schema * @param node - JsonNode to minimize * @param schemaName - name of schema to use * @note for minimizing data must be valid against given schema */ DLL_LINKAGE void minimize(JsonNode & node, const std::string & schemaName); /// opposed to minimize, adds all missing, required entries that have default value DLL_LINKAGE void maximize(JsonNode & node, const std::string & schemaName); /** * @brief validate node against specified schema * @param node - JsonNode to check * @param schemaName - name of schema to use * @param dataName - some way to identify data (printed in console in case of errors) * @returns true if data in node fully compilant with schema */ DLL_LINKAGE bool validate(const JsonNode & node, const std::string & schemaName, const std::string & dataName); /// get schema by json URI: vcmi:# /// example: schema "vcmi:settings" is used to check user settings DLL_LINKAGE const JsonNode & getSchema(const std::string & URI); /// for easy construction of JsonNodes; helps with inserting primitives into vector node DLL_LINKAGE JsonNode boolNode(bool value); DLL_LINKAGE JsonNode floatNode(double value); DLL_LINKAGE JsonNode stringNode(const std::string & value); DLL_LINKAGE JsonNode intNode(si64 value); } namespace JsonDetail { // conversion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++) template struct JsonConvImpl; template struct JsonConvImpl { static T convertImpl(const JsonNode & node) { return T((int)node.Float()); } }; template struct JsonConvImpl { static T convertImpl(const JsonNode & node) { return T(node.Float()); } }; template struct JsonConverter { static Type convert(const JsonNode & node) { ///this should be triggered only for numeric types and enums static_assert(boost::mpl::or_, std::is_enum, boost::is_class >::value, "Unsupported type for JsonNode::convertTo()!"); return JsonConvImpl, boost::is_class >::value >::convertImpl(node); } }; template struct JsonConverter > { static std::map convert(const JsonNode & node) { std::map ret; for (const JsonMap::value_type & entry : node.Struct()) { ret.insert(entry.first, entry.second.convertTo()); } return ret; } }; template struct JsonConverter > { static std::set convert(const JsonNode & node) { std::set ret; for(const JsonVector::value_type & entry : node.Vector()) { ret.insert(entry.convertTo()); } return ret; } }; template struct JsonConverter > { static std::vector convert(const JsonNode & node) { std::vector ret; for (const JsonVector::value_type & entry: node.Vector()) { ret.push_back(entry.convertTo()); } return ret; } }; template<> struct JsonConverter { static std::string convert(const JsonNode & node) { return node.String(); } }; template<> struct JsonConverter { static bool convert(const JsonNode & node) { return node.Bool(); } }; } template Type JsonNode::convertTo() const { return JsonDetail::JsonConverter::convert(*this); } VCMI_LIB_NAMESPACE_END