JsonValidator.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*
  2. * JsonValidator.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "JsonValidator.h"
  12. #include "JsonUtils.h"
  13. #include "../VCMI_Lib.h"
  14. #include "../filesystem/Filesystem.h"
  15. #include "../modding/ModScope.h"
  16. #include "../modding/CModHandler.h"
  17. #include "../ScopeGuard.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. static std::string emptyCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  20. {
  21. // check is not needed - e.g. incorporated into another check
  22. return "";
  23. }
  24. static std::string notImplementedCheck(JsonValidator & validator,
  25. const JsonNode & baseSchema,
  26. const JsonNode & schema,
  27. const JsonNode & data)
  28. {
  29. return "Not implemented entry in schema";
  30. }
  31. static std::string schemaListCheck(JsonValidator & validator,
  32. const JsonNode & baseSchema,
  33. const JsonNode & schema,
  34. const JsonNode & data,
  35. const std::string & errorMsg,
  36. const std::function<bool(size_t)> & isValid)
  37. {
  38. std::string errors = "<tested schemas>\n";
  39. size_t result = 0;
  40. for(const auto & schemaEntry : schema.Vector())
  41. {
  42. std::string error = validator.check(schemaEntry, data);
  43. if (error.empty())
  44. {
  45. result++;
  46. }
  47. else
  48. {
  49. errors += error;
  50. errors += "<end of schema>\n";
  51. }
  52. }
  53. if (isValid(result))
  54. return "";
  55. else
  56. return validator.makeErrorMessage(errorMsg) + errors;
  57. }
  58. static std::string allOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  59. {
  60. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&schema](size_t count)
  61. {
  62. return count == schema.Vector().size();
  63. });
  64. }
  65. static std::string anyOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  66. {
  67. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [](size_t count)
  68. {
  69. return count > 0;
  70. });
  71. }
  72. static std::string oneOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  73. {
  74. return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [](size_t count)
  75. {
  76. return count == 1;
  77. });
  78. }
  79. static std::string notCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  80. {
  81. if (validator.check(schema, data).empty())
  82. return validator.makeErrorMessage("Successful validation against negative check");
  83. return "";
  84. }
  85. static std::string enumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  86. {
  87. for(const auto & enumEntry : schema.Vector())
  88. {
  89. if (data == enumEntry)
  90. return "";
  91. }
  92. return validator.makeErrorMessage("Key must have one of predefined values");
  93. }
  94. static std::string typeCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  95. {
  96. static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
  97. {
  98. {"null", JsonNode::JsonType::DATA_NULL},
  99. {"boolean", JsonNode::JsonType::DATA_BOOL},
  100. {"number", JsonNode::JsonType::DATA_FLOAT},
  101. {"integer", JsonNode::JsonType::DATA_INTEGER},
  102. {"string", JsonNode::JsonType::DATA_STRING},
  103. {"array", JsonNode::JsonType::DATA_VECTOR},
  104. {"object", JsonNode::JsonType::DATA_STRUCT}
  105. };
  106. const auto & typeName = schema.String();
  107. auto it = stringToType.find(typeName);
  108. if(it == stringToType.end())
  109. {
  110. return validator.makeErrorMessage("Unknown type in schema:" + typeName);
  111. }
  112. JsonNode::JsonType type = it->second;
  113. // for "number" type both float and integer are allowed
  114. if(type == JsonNode::JsonType::DATA_FLOAT && data.isNumber())
  115. return "";
  116. if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
  117. return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
  118. return "";
  119. }
  120. static std::string refCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  121. {
  122. std::string URI = schema.String();
  123. //node must be validated using schema pointed by this reference and not by data here
  124. //Local reference. Turn it into more easy to handle remote ref
  125. if (boost::algorithm::starts_with(URI, "#"))
  126. {
  127. const std::string name = validator.usedSchemas.back();
  128. const std::string nameClean = name.substr(0, name.find('#'));
  129. URI = nameClean + URI;
  130. }
  131. return validator.check(URI, data);
  132. }
  133. static std::string formatCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  134. {
  135. auto formats = validator.getKnownFormats();
  136. std::string errors;
  137. auto checker = formats.find(schema.String());
  138. if (checker != formats.end())
  139. {
  140. if (data.isString())
  141. {
  142. std::string result = checker->second(data);
  143. if (!result.empty())
  144. errors += validator.makeErrorMessage(result);
  145. }
  146. else
  147. {
  148. errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
  149. }
  150. }
  151. else
  152. errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
  153. return errors;
  154. }
  155. static std::string maxLengthCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  156. {
  157. if (data.String().size() > schema.Float())
  158. return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
  159. return "";
  160. }
  161. static std::string minLengthCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  162. {
  163. if (data.String().size() < schema.Float())
  164. return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
  165. return "";
  166. }
  167. static std::string maximumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  168. {
  169. if (data.Float() > schema.Float())
  170. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  171. return "";
  172. }
  173. static std::string minimumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  174. {
  175. if (data.Float() < schema.Float())
  176. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  177. return "";
  178. }
  179. static std::string exclusiveMaximumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  180. {
  181. if (data.Float() >= schema.Float())
  182. return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
  183. return "";
  184. }
  185. static std::string exclusiveMinimumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  186. {
  187. if (data.Float() <= schema.Float())
  188. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  189. return "";
  190. }
  191. static std::string multipleOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  192. {
  193. double result = data.Integer() / schema.Integer();
  194. if (!vstd::isAlmostEqual(floor(result), result))
  195. return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
  196. return "";
  197. }
  198. static std::string itemEntryCheck(JsonValidator & validator, const JsonVector & items, const JsonNode & schema, size_t index)
  199. {
  200. validator.currentPath.emplace_back();
  201. validator.currentPath.back().Float() = static_cast<double>(index);
  202. auto onExit = vstd::makeScopeGuard([&validator]()
  203. {
  204. validator.currentPath.pop_back();
  205. });
  206. if (!schema.isNull())
  207. return validator.check(schema, items[index]);
  208. return "";
  209. }
  210. static std::string itemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  211. {
  212. std::string errors;
  213. for (size_t i=0; i<data.Vector().size(); i++)
  214. {
  215. if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
  216. {
  217. if (schema.Vector().size() > i)
  218. errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
  219. }
  220. else
  221. {
  222. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  223. }
  224. }
  225. return errors;
  226. }
  227. static std::string additionalItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  228. {
  229. std::string errors;
  230. // "items" is struct or empty (defaults to empty struct) - validation always successful
  231. const JsonNode & items = baseSchema["items"];
  232. if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
  233. return "";
  234. for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
  235. {
  236. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  237. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  238. else if(!schema.isNull() && !schema.Bool())
  239. errors += validator.makeErrorMessage("Unknown entry found");
  240. }
  241. return errors;
  242. }
  243. static std::string minItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  244. {
  245. if (data.Vector().size() < schema.Float())
  246. return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
  247. return "";
  248. }
  249. static std::string maxItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  250. {
  251. if (data.Vector().size() > schema.Float())
  252. return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
  253. return "";
  254. }
  255. static std::string uniqueItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  256. {
  257. if (schema.Bool())
  258. {
  259. for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
  260. {
  261. auto itB = itA;
  262. while (++itB != schema.Vector().end())
  263. {
  264. if (*itA == *itB)
  265. return validator.makeErrorMessage("List must consist from unique items");
  266. }
  267. }
  268. }
  269. return "";
  270. }
  271. static std::string maxPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  272. {
  273. if (data.Struct().size() > schema.Float())
  274. return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
  275. return "";
  276. }
  277. static std::string minPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  278. {
  279. if (data.Struct().size() < schema.Float())
  280. return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
  281. return "";
  282. }
  283. static std::string uniquePropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  284. {
  285. for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
  286. {
  287. auto itB = itA;
  288. while (++itB != data.Struct().end())
  289. {
  290. if (itA->second == itB->second)
  291. return validator.makeErrorMessage("List must consist from unique items");
  292. }
  293. }
  294. return "";
  295. }
  296. static std::string requiredCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  297. {
  298. std::string errors;
  299. for(const auto & required : schema.Vector())
  300. {
  301. if (data[required.String()].isNull())
  302. errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
  303. }
  304. return errors;
  305. }
  306. static std::string dependenciesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  307. {
  308. std::string errors;
  309. for(const auto & deps : schema.Struct())
  310. {
  311. if (!data[deps.first].isNull())
  312. {
  313. if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
  314. {
  315. JsonVector depList = deps.second.Vector();
  316. for(auto & depEntry : depList)
  317. {
  318. if (data[depEntry.String()].isNull())
  319. errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
  320. }
  321. }
  322. else
  323. {
  324. if (!validator.check(deps.second, data).empty())
  325. errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
  326. }
  327. }
  328. }
  329. return errors;
  330. }
  331. static std::string propertyEntryCheck(JsonValidator & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
  332. {
  333. validator.currentPath.emplace_back();
  334. validator.currentPath.back().String() = nodeName;
  335. auto onExit = vstd::makeScopeGuard([&validator]()
  336. {
  337. validator.currentPath.pop_back();
  338. });
  339. // there is schema specifically for this item
  340. if (!schema.isNull())
  341. return validator.check(schema, node);
  342. return "";
  343. }
  344. static std::string propertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  345. {
  346. std::string errors;
  347. for(const auto & entry : data.Struct())
  348. errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
  349. return errors;
  350. }
  351. static std::string additionalPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  352. {
  353. std::string errors;
  354. for(const auto & entry : data.Struct())
  355. {
  356. if (baseSchema["properties"].Struct().count(entry.first) == 0)
  357. {
  358. // try generic additionalItems schema
  359. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  360. errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
  361. // or, additionalItems field can be bool which indicates if such items are allowed
  362. else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
  363. errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
  364. }
  365. }
  366. return errors;
  367. }
  368. static bool testFilePresence(const std::string & scope, const ResourcePath & resource)
  369. {
  370. std::set<std::string> allowedScopes;
  371. if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
  372. {
  373. //NOTE: recursive dependencies are not allowed at the moment - update code if this changes
  374. bool found = true;
  375. allowedScopes = VLC->modh->getModDependencies(scope, found);
  376. if(!found)
  377. return false;
  378. allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
  379. }
  380. allowedScopes.insert(scope); // mods can use their own files
  381. for(const auto & entry : allowedScopes)
  382. {
  383. if (CResourceHandler::get(entry)->existsResource(resource))
  384. return true;
  385. }
  386. return false;
  387. }
  388. #define TEST_FILE(scope, prefix, file, type) \
  389. if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
  390. return ""
  391. static std::string testAnimation(const std::string & path, const std::string & scope)
  392. {
  393. TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
  394. TEST_FILE(scope, "Sprites/", path, EResType::JSON);
  395. return "Animation file \"" + path + "\" was not found";
  396. }
  397. static std::string textFile(const JsonNode & node)
  398. {
  399. TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
  400. return "Text file \"" + node.String() + "\" was not found";
  401. }
  402. static std::string musicFile(const JsonNode & node)
  403. {
  404. TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
  405. TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
  406. return "Music file \"" + node.String() + "\" was not found";
  407. }
  408. static std::string soundFile(const JsonNode & node)
  409. {
  410. TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
  411. return "Sound file \"" + node.String() + "\" was not found";
  412. }
  413. static std::string animationFile(const JsonNode & node)
  414. {
  415. return testAnimation(node.String(), node.getModScope());
  416. }
  417. static std::string imageFile(const JsonNode & node)
  418. {
  419. TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
  420. TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
  421. if (node.String().find(':') != std::string::npos)
  422. return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
  423. return "Image file \"" + node.String() + "\" was not found";
  424. }
  425. static std::string videoFile(const JsonNode & node)
  426. {
  427. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
  428. return "Video file \"" + node.String() + "\" was not found";
  429. }
  430. #undef TEST_FILE
  431. JsonValidator::TValidatorMap createCommonFields()
  432. {
  433. JsonValidator::TValidatorMap ret;
  434. ret["format"] = formatCheck;
  435. ret["allOf"] = allOfCheck;
  436. ret["anyOf"] = anyOfCheck;
  437. ret["oneOf"] = oneOfCheck;
  438. ret["enum"] = enumCheck;
  439. ret["type"] = typeCheck;
  440. ret["not"] = notCheck;
  441. ret["$ref"] = refCheck;
  442. // fields that don't need implementation
  443. ret["title"] = emptyCheck;
  444. ret["$schema"] = emptyCheck;
  445. ret["default"] = emptyCheck;
  446. ret["defaultIOS"] = emptyCheck;
  447. ret["defaultAndroid"] = emptyCheck;
  448. ret["defaultWindows"] = emptyCheck;
  449. ret["description"] = emptyCheck;
  450. ret["definitions"] = emptyCheck;
  451. // Not implemented
  452. ret["propertyNames"] = notImplementedCheck;
  453. ret["contains"] = notImplementedCheck;
  454. ret["const"] = notImplementedCheck;
  455. ret["examples"] = notImplementedCheck;
  456. return ret;
  457. }
  458. JsonValidator::TValidatorMap createStringFields()
  459. {
  460. JsonValidator::TValidatorMap ret = createCommonFields();
  461. ret["maxLength"] = maxLengthCheck;
  462. ret["minLength"] = minLengthCheck;
  463. ret["pattern"] = notImplementedCheck;
  464. return ret;
  465. }
  466. JsonValidator::TValidatorMap createNumberFields()
  467. {
  468. JsonValidator::TValidatorMap ret = createCommonFields();
  469. ret["maximum"] = maximumCheck;
  470. ret["minimum"] = minimumCheck;
  471. ret["multipleOf"] = multipleOfCheck;
  472. ret["exclusiveMaximum"] = exclusiveMaximumCheck;
  473. ret["exclusiveMinimum"] = exclusiveMinimumCheck;
  474. return ret;
  475. }
  476. JsonValidator::TValidatorMap createVectorFields()
  477. {
  478. JsonValidator::TValidatorMap ret = createCommonFields();
  479. ret["items"] = itemsCheck;
  480. ret["minItems"] = minItemsCheck;
  481. ret["maxItems"] = maxItemsCheck;
  482. ret["uniqueItems"] = uniqueItemsCheck;
  483. ret["additionalItems"] = additionalItemsCheck;
  484. return ret;
  485. }
  486. JsonValidator::TValidatorMap createStructFields()
  487. {
  488. JsonValidator::TValidatorMap ret = createCommonFields();
  489. ret["additionalProperties"] = additionalPropertiesCheck;
  490. ret["uniqueProperties"] = uniquePropertiesCheck;
  491. ret["maxProperties"] = maxPropertiesCheck;
  492. ret["minProperties"] = minPropertiesCheck;
  493. ret["dependencies"] = dependenciesCheck;
  494. ret["properties"] = propertiesCheck;
  495. ret["required"] = requiredCheck;
  496. ret["patternProperties"] = notImplementedCheck;
  497. return ret;
  498. }
  499. JsonValidator::TFormatMap createFormatMap()
  500. {
  501. JsonValidator::TFormatMap ret;
  502. ret["textFile"] = textFile;
  503. ret["musicFile"] = musicFile;
  504. ret["soundFile"] = soundFile;
  505. ret["animationFile"] = animationFile;
  506. ret["imageFile"] = imageFile;
  507. ret["videoFile"] = videoFile;
  508. //TODO:
  509. // uri-reference
  510. // uri-template
  511. // json-pointer
  512. return ret;
  513. }
  514. std::string JsonValidator::makeErrorMessage(const std::string &message)
  515. {
  516. std::string errors;
  517. errors += "At ";
  518. if (!currentPath.empty())
  519. {
  520. for(const JsonNode &path : currentPath)
  521. {
  522. errors += "/";
  523. if (path.getType() == JsonNode::JsonType::DATA_STRING)
  524. errors += path.String();
  525. else
  526. errors += std::to_string(static_cast<unsigned>(path.Float()));
  527. }
  528. }
  529. else
  530. errors += "<root>";
  531. errors += "\n\t Error: " + message + "\n";
  532. return errors;
  533. }
  534. std::string JsonValidator::check(const std::string & schemaName, const JsonNode & data)
  535. {
  536. usedSchemas.push_back(schemaName);
  537. auto onscopeExit = vstd::makeScopeGuard([this]()
  538. {
  539. usedSchemas.pop_back();
  540. });
  541. return check(JsonUtils::getSchema(schemaName), data);
  542. }
  543. std::string JsonValidator::check(const JsonNode & schema, const JsonNode & data)
  544. {
  545. const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
  546. std::string errors;
  547. for(const auto & entry : schema.Struct())
  548. {
  549. auto checker = knownFields.find(entry.first);
  550. if (checker != knownFields.end())
  551. errors += checker->second(*this, schema, entry.second, data);
  552. }
  553. return errors;
  554. }
  555. const JsonValidator::TValidatorMap & JsonValidator::getKnownFieldsFor(JsonNode::JsonType type)
  556. {
  557. static const TValidatorMap commonFields = createCommonFields();
  558. static const TValidatorMap numberFields = createNumberFields();
  559. static const TValidatorMap stringFields = createStringFields();
  560. static const TValidatorMap vectorFields = createVectorFields();
  561. static const TValidatorMap structFields = createStructFields();
  562. switch (type)
  563. {
  564. case JsonNode::JsonType::DATA_FLOAT:
  565. case JsonNode::JsonType::DATA_INTEGER:
  566. return numberFields;
  567. case JsonNode::JsonType::DATA_STRING: return stringFields;
  568. case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
  569. case JsonNode::JsonType::DATA_STRUCT: return structFields;
  570. default: return commonFields;
  571. }
  572. }
  573. const JsonValidator::TFormatMap & JsonValidator::getKnownFormats()
  574. {
  575. static const TFormatMap knownFormats = createFormatMap();
  576. return knownFormats;
  577. }
  578. VCMI_LIB_NAMESPACE_END