JsonValidator.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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 constCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  95. {
  96. if (data == schema)
  97. return "";
  98. return validator.makeErrorMessage("Key must have have constant value");
  99. }
  100. static std::string typeCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  101. {
  102. static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
  103. {
  104. {"null", JsonNode::JsonType::DATA_NULL},
  105. {"boolean", JsonNode::JsonType::DATA_BOOL},
  106. {"number", JsonNode::JsonType::DATA_FLOAT},
  107. {"integer", JsonNode::JsonType::DATA_INTEGER},
  108. {"string", JsonNode::JsonType::DATA_STRING},
  109. {"array", JsonNode::JsonType::DATA_VECTOR},
  110. {"object", JsonNode::JsonType::DATA_STRUCT}
  111. };
  112. const auto & typeName = schema.String();
  113. auto it = stringToType.find(typeName);
  114. if(it == stringToType.end())
  115. {
  116. return validator.makeErrorMessage("Unknown type in schema:" + typeName);
  117. }
  118. JsonNode::JsonType type = it->second;
  119. // for "number" type both float and integer are allowed
  120. if(type == JsonNode::JsonType::DATA_FLOAT && data.isNumber())
  121. return "";
  122. if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
  123. return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
  124. return "";
  125. }
  126. static std::string refCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  127. {
  128. std::string URI = schema.String();
  129. //node must be validated using schema pointed by this reference and not by data here
  130. //Local reference. Turn it into more easy to handle remote ref
  131. if (boost::algorithm::starts_with(URI, "#"))
  132. {
  133. const std::string name = validator.usedSchemas.back();
  134. const std::string nameClean = name.substr(0, name.find('#'));
  135. URI = nameClean + URI;
  136. }
  137. return validator.check(URI, data);
  138. }
  139. static std::string formatCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  140. {
  141. auto formats = validator.getKnownFormats();
  142. std::string errors;
  143. auto checker = formats.find(schema.String());
  144. if (checker != formats.end())
  145. {
  146. if (data.isString())
  147. {
  148. std::string result = checker->second(data);
  149. if (!result.empty())
  150. errors += validator.makeErrorMessage(result);
  151. }
  152. else
  153. {
  154. errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
  155. }
  156. }
  157. else
  158. errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
  159. return errors;
  160. }
  161. static std::string maxLengthCheck(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 longer than %d symbols") % schema.Float()).str());
  165. return "";
  166. }
  167. static std::string minLengthCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  168. {
  169. if (data.String().size() < schema.Float())
  170. return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
  171. return "";
  172. }
  173. static std::string maximumCheck(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 bigger than %d") % schema.Float()).str());
  177. return "";
  178. }
  179. static std::string minimumCheck(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 smaller than %d") % schema.Float()).str());
  183. return "";
  184. }
  185. static std::string exclusiveMaximumCheck(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 bigger than %d") % schema.Float()).str());
  189. return "";
  190. }
  191. static std::string exclusiveMinimumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  192. {
  193. if (data.Float() <= schema.Float())
  194. return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
  195. return "";
  196. }
  197. static std::string multipleOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  198. {
  199. double result = data.Integer() / schema.Integer();
  200. if (!vstd::isAlmostEqual(floor(result), result))
  201. return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
  202. return "";
  203. }
  204. static std::string itemEntryCheck(JsonValidator & validator, const JsonVector & items, const JsonNode & schema, size_t index)
  205. {
  206. validator.currentPath.emplace_back();
  207. validator.currentPath.back().Float() = static_cast<double>(index);
  208. auto onExit = vstd::makeScopeGuard([&validator]()
  209. {
  210. validator.currentPath.pop_back();
  211. });
  212. if (!schema.isNull())
  213. return validator.check(schema, items[index]);
  214. return "";
  215. }
  216. static std::string itemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  217. {
  218. std::string errors;
  219. for (size_t i=0; i<data.Vector().size(); i++)
  220. {
  221. if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
  222. {
  223. if (schema.Vector().size() > i)
  224. errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
  225. }
  226. else
  227. {
  228. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  229. }
  230. }
  231. return errors;
  232. }
  233. static std::string additionalItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  234. {
  235. std::string errors;
  236. // "items" is struct or empty (defaults to empty struct) - validation always successful
  237. const JsonNode & items = baseSchema["items"];
  238. if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
  239. return "";
  240. for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
  241. {
  242. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  243. errors += itemEntryCheck(validator, data.Vector(), schema, i);
  244. else if(!schema.isNull() && !schema.Bool())
  245. errors += validator.makeErrorMessage("Unknown entry found");
  246. }
  247. return errors;
  248. }
  249. static std::string minItemsCheck(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 smaller than %d") % schema.Float()).str());
  253. return "";
  254. }
  255. static std::string maxItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  256. {
  257. if (data.Vector().size() > schema.Float())
  258. return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
  259. return "";
  260. }
  261. static std::string uniqueItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  262. {
  263. if (schema.Bool())
  264. {
  265. for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
  266. {
  267. auto itB = itA;
  268. while (++itB != schema.Vector().end())
  269. {
  270. if (*itA == *itB)
  271. return validator.makeErrorMessage("List must consist from unique items");
  272. }
  273. }
  274. }
  275. return "";
  276. }
  277. static std::string maxPropertiesCheck(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 bigger than %d") % schema.Float()).str());
  281. return "";
  282. }
  283. static std::string minPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  284. {
  285. if (data.Struct().size() < schema.Float())
  286. return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
  287. return "";
  288. }
  289. static std::string uniquePropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  290. {
  291. for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
  292. {
  293. auto itB = itA;
  294. while (++itB != data.Struct().end())
  295. {
  296. if (itA->second == itB->second)
  297. return validator.makeErrorMessage("List must consist from unique items");
  298. }
  299. }
  300. return "";
  301. }
  302. static std::string requiredCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  303. {
  304. std::string errors;
  305. for(const auto & required : schema.Vector())
  306. {
  307. if (data[required.String()].isNull())
  308. errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
  309. }
  310. return errors;
  311. }
  312. static std::string dependenciesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  313. {
  314. std::string errors;
  315. for(const auto & deps : schema.Struct())
  316. {
  317. if (!data[deps.first].isNull())
  318. {
  319. if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
  320. {
  321. JsonVector depList = deps.second.Vector();
  322. for(auto & depEntry : depList)
  323. {
  324. if (data[depEntry.String()].isNull())
  325. errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
  326. }
  327. }
  328. else
  329. {
  330. if (!validator.check(deps.second, data).empty())
  331. errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
  332. }
  333. }
  334. }
  335. return errors;
  336. }
  337. static std::string propertyEntryCheck(JsonValidator & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
  338. {
  339. validator.currentPath.emplace_back();
  340. validator.currentPath.back().String() = nodeName;
  341. auto onExit = vstd::makeScopeGuard([&validator]()
  342. {
  343. validator.currentPath.pop_back();
  344. });
  345. // there is schema specifically for this item
  346. if (!schema.isNull())
  347. return validator.check(schema, node);
  348. return "";
  349. }
  350. static std::string propertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  351. {
  352. std::string errors;
  353. for(const auto & entry : data.Struct())
  354. errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
  355. return errors;
  356. }
  357. static std::string additionalPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
  358. {
  359. std::string errors;
  360. for(const auto & entry : data.Struct())
  361. {
  362. if (baseSchema["properties"].Struct().count(entry.first) == 0)
  363. {
  364. // try generic additionalItems schema
  365. if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
  366. errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
  367. // or, additionalItems field can be bool which indicates if such items are allowed
  368. else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
  369. errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
  370. }
  371. }
  372. return errors;
  373. }
  374. static bool testFilePresence(const std::string & scope, const ResourcePath & resource)
  375. {
  376. #ifndef ENABLE_MINIMAL_LIB
  377. std::set<std::string> allowedScopes;
  378. if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
  379. {
  380. //NOTE: recursive dependencies are not allowed at the moment - update code if this changes
  381. bool found = true;
  382. allowedScopes = VLC->modh->getModDependencies(scope, found);
  383. if(!found)
  384. return false;
  385. allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
  386. }
  387. allowedScopes.insert(scope); // mods can use their own files
  388. for(const auto & entry : allowedScopes)
  389. {
  390. if (CResourceHandler::get(entry)->existsResource(resource))
  391. return true;
  392. }
  393. #endif
  394. return false;
  395. }
  396. #define TEST_FILE(scope, prefix, file, type) \
  397. if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
  398. return ""
  399. static std::string testAnimation(const std::string & path, const std::string & scope)
  400. {
  401. TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
  402. TEST_FILE(scope, "Sprites/", path, EResType::JSON);
  403. return "Animation file \"" + path + "\" was not found";
  404. }
  405. static std::string textFile(const JsonNode & node)
  406. {
  407. TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
  408. return "Text file \"" + node.String() + "\" was not found";
  409. }
  410. static std::string musicFile(const JsonNode & node)
  411. {
  412. TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
  413. TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
  414. return "Music file \"" + node.String() + "\" was not found";
  415. }
  416. static std::string soundFile(const JsonNode & node)
  417. {
  418. TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
  419. return "Sound file \"" + node.String() + "\" was not found";
  420. }
  421. static std::string animationFile(const JsonNode & node)
  422. {
  423. return testAnimation(node.String(), node.getModScope());
  424. }
  425. static std::string imageFile(const JsonNode & node)
  426. {
  427. TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
  428. TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
  429. if (node.String().find(':') != std::string::npos)
  430. return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
  431. return "Image file \"" + node.String() + "\" was not found";
  432. }
  433. static std::string videoFile(const JsonNode & node)
  434. {
  435. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
  436. TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO_LOW_QUALITY);
  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