JsonValidator.cpp 21 KB

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