test_json_parser.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. // ----------------------------------------------------------------------------
  2. // Copyright (C) 2002-2006 Marcin Kalicinski
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. // For more information, see www.boost.org
  9. // ----------------------------------------------------------------------------
  10. #include "test_utils.hpp"
  11. #include <boost/property_tree/json_parser.hpp>
  12. ///////////////////////////////////////////////////////////////////////////////
  13. // Test data
  14. const char *ok_data_1 =
  15. "{}";
  16. const char *ok_data_2 =
  17. " \t {\n"
  18. " \t \"name 0\" : \"value\", \t \n"
  19. " \t \"name 1\" : \"\", \n"
  20. " \t \"name 2\" : true, \n"
  21. " \t \"name 3\" : false, \n"
  22. " \t \"name 4\" : null, \n"
  23. " \t \"name 5\" : 0, \n"
  24. " \t \"name 6\" : -5, \n"
  25. " \t \"name 7\" : 1.1, \n"
  26. " \t \"name 8\" : -956.45e+4, \n"
  27. " \t \"name 8\" : 5956.45E-11, \n"
  28. " \t \"name 9\" : [1,2,3,4], \n"
  29. " \t \"name 10\" : {\"a\":\"b\"} \n"
  30. " \t } \n";
  31. const char *ok_data_3 =
  32. "{\"a\":{\"b\":\"c\"}}";
  33. const char *ok_data_4 =
  34. "{\"a\":[{\"b\":\"c\"},{\"d\":\"e\"},1,2,3,4],\"f\":null}";
  35. const char *ok_data_6 =
  36. "{\"menu\": {\n"
  37. " \"header\": \"SVG Viewer\",\n"
  38. " \"items\": [\n"
  39. " {\"id\": \"Open\"},\n"
  40. " {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n"
  41. " null,\n"
  42. " {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n"
  43. " {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n"
  44. " {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n"
  45. " null,\n"
  46. " {\"id\": \"Quality\"},\n"
  47. " {\"id\": \"Pause\"},\n"
  48. " {\"id\": \"Mute\"},\n"
  49. " null,\n"
  50. " {\"id\": \"Find\", \"label\": \"Find...\"},\n"
  51. " {\"id\": \"FindAgain\", \"label\": \"Find Again\"},\n"
  52. " {\"id\": \"Copy\"},\n"
  53. " {\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},\n"
  54. " {\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},\n"
  55. " {\"id\": \"ViewSVG\", \"label\": \"View SVG\"},\n"
  56. " {\"id\": \"ViewSource\", \"label\": \"View Source\"},\n"
  57. " {\"id\": \"SaveAs\", \"label\": \"Save As\"},\n"
  58. " null,\n"
  59. " {\"id\": \"Help\"},\n"
  60. " {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n"
  61. " ]\n"
  62. "}}\n";
  63. const char *ok_data_7 =
  64. "{\"web-app\": {\n"
  65. " \"servlet\": [ \n"
  66. " {\n"
  67. " \"servlet-name\": \"cofaxCDS\",\n"
  68. " \"servlet-class\": \"org.cofax.cds.CDSServlet\",\n"
  69. " \"init-param\": {\n"
  70. " \"configGlossary:installationAt\": \"Philadelphia, PA\",\n"
  71. " \"configGlossary:adminEmail\": \"ksm@pobox.com\",\n"
  72. " \"configGlossary:poweredBy\": \"Cofax\",\n"
  73. " \"configGlossary:poweredByIcon\": \"/images/cofax.gif\",\n"
  74. " \"configGlossary:staticPath\": \"/content/static\",\n"
  75. " \"templateProcessorClass\": \"org.cofax.WysiwygTemplate\",\n"
  76. " \"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\",\n"
  77. " \"templatePath\": \"templates\",\n"
  78. " \"templateOverridePath\": \"\",\n"
  79. " \"defaultListTemplate\": \"listTemplate.htm\",\n"
  80. " \"defaultFileTemplate\": \"articleTemplate.htm\",\n"
  81. " \"useJSP\": false,\n"
  82. " \"jspListTemplate\": \"listTemplate.jsp\",\n"
  83. " \"jspFileTemplate\": \"articleTemplate.jsp\",\n"
  84. " \"cachePackageTagsTrack\": 200,\n"
  85. " \"cachePackageTagsStore\": 200,\n"
  86. " \"cachePackageTagsRefresh\": 60,\n"
  87. " \"cacheTemplatesTrack\": 100,\n"
  88. " \"cacheTemplatesStore\": 50,\n"
  89. " \"cacheTemplatesRefresh\": 15,\n"
  90. " \"cachePagesTrack\": 200,\n"
  91. " \"cachePagesStore\": 100,\n"
  92. " \"cachePagesRefresh\": 10,\n"
  93. " \"cachePagesDirtyRead\": 10,\n"
  94. " \"searchEngineListTemplate\": \"forSearchEnginesList.htm\",\n"
  95. " \"searchEngineFileTemplate\": \"forSearchEngines.htm\",\n"
  96. " \"searchEngineRobotsDb\": \"WEB-INF/robots.db\",\n"
  97. " \"useDataStore\": true,\n"
  98. " \"dataStoreClass\": \"org.cofax.SqlDataStore\",\n"
  99. " \"redirectionClass\": \"org.cofax.SqlRedirection\",\n"
  100. " \"dataStoreName\": \"cofax\",\n"
  101. " \"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\",\n"
  102. " \"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\",\n"
  103. " \"dataStoreUser\": \"sa\",\n"
  104. " \"dataStorePassword\": \"dataStoreTestQuery\",\n"
  105. " \"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\",\n"
  106. " \"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\",\n"
  107. " \"dataStoreInitConns\": 10,\n"
  108. " \"dataStoreMaxConns\": 100,\n"
  109. " \"dataStoreConnUsageLimit\": 100,\n"
  110. " \"dataStoreLogLevel\": \"debug\",\n"
  111. " \"maxUrlLength\": 500}},\n"
  112. " {\n"
  113. " \"servlet-name\": \"cofaxEmail\",\n"
  114. "\n \"servlet-class\": \"org.cofax.cds.EmailServlet\",\n"
  115. " \"init-param\": {\n"
  116. " \"mailHost\": \"mail1\",\n"
  117. " \"mailHostOverride\": \"mail2\"}},\n"
  118. " {\n"
  119. " \"servlet-name\": \"cofaxAdmin\",\n"
  120. " \"servlet-class\": \"org.cofax.cds.AdminServlet\"},\n"
  121. " \n"
  122. " {\n"
  123. " \"servlet-name\": \"fileServlet\",\n"
  124. " \"servlet-class\": \"org.cofax.cds.FileServlet\"},\n"
  125. " {\n"
  126. " \"servlet-name\": \"cofaxTools\",\n"
  127. " \"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\",\n"
  128. " \"init-param\": {\n"
  129. " \"templatePath\": \"toolstemplates/\",\n"
  130. " \"log\": 1,\n"
  131. " \"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\",\n"
  132. " \"logMaxSize\": \"\",\n"
  133. " \"dataLog\": 1,\n"
  134. " \"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\",\n"
  135. " \"dataLogMaxSize\": \"\",\n"
  136. " \"removePageCache\": \"/content/admin/remove?cache=pages&id=\",\n"
  137. " \"removeTemplateCache\": \"/content/admin/remove?cache=templates&id=\",\n"
  138. " \"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\",\n"
  139. " \"lookInContext\": 1,\n"
  140. " \"adminGroupID\": 4,\n"
  141. " \"betaServer\": true}}],\n"
  142. " \"servlet-mapping\": {\n"
  143. " \"cofaxCDS\": \"/\",\n"
  144. " \"cofaxEmail\": \"/cofaxutil/aemail/*\",\n"
  145. " \"cofaxAdmin\": \"/admin/*\",\n"
  146. " \"fileServlet\": \"/static/*\",\n"
  147. " \"cofaxTools\": \"/tools/*\"},\n"
  148. " \n"
  149. " \"taglib\": {\n"
  150. " \"taglib-uri\": \"cofax.tld\",\n"
  151. " \"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}\n";
  152. const char *ok_data_8 =
  153. "{\"widget\": {\n"
  154. " \"debug\": \"on\",\n"
  155. " \"window\": {\n"
  156. " \"title\": \"Sample Konfabulator Widget\", \"name\": \"main_window\", \"width\": 500, \"height\": 500\n"
  157. " }, \"image\": { \n"
  158. " \"src\": \"Images/Sun.png\",\n"
  159. " \"name\": \"sun1\", \"hOffset\": 250, \"vOffset\": 250, \"alignment\": \"center\"\n"
  160. " }, \"text\": {\n"
  161. " \"data\": \"Click Here\",\n"
  162. " \"size\": 36,\n"
  163. " \"style\": \"bold\", \"name\": \"text1\", \"hOffset\": 250, \"vOffset\": 100, \"alignment\": \"center\",\n"
  164. " \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n"
  165. " }\n"
  166. "}} \n";
  167. const char *ok_data_9 =
  168. "{\"menu\": {\n"
  169. " \"id\": \"file\",\n"
  170. " \"value\": \"File:\",\n"
  171. " \"popup\": {\n"
  172. " \"menuitem\": [\n"
  173. " {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n"
  174. " {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n"
  175. "\n {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n"
  176. " ]\n"
  177. " }\n"
  178. "}}\n";
  179. const char *ok_data_10 =
  180. "{\n"
  181. " \"glossary\": {\n"
  182. " \"title\": \"example glossary\",\n"
  183. " \"GlossDiv\": {\n"
  184. " \"title\": \"S\",\n"
  185. " \"GlossList\": [{\n"
  186. " \"ID\": \"SGML\",\n"
  187. " \"SortAs\": \"SGML\",\n"
  188. " \"GlossTerm\": \"Standard Generalized Markup Language\",\n"
  189. " \"Acronym\": \"SGML\",\n"
  190. " \"Abbrev\": \"ISO 8879:1986\",\n"
  191. " \"GlossDef\": \n"
  192. "\"A meta-markup language, used to create markup languages such as DocBook.\",\n"
  193. " \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"]\n"
  194. " }]\n"
  195. " }\n"
  196. " }\n"
  197. "}\n";
  198. const char *ok_data_11 =
  199. "{\n"
  200. " \"data\" : [\n"
  201. " {\n"
  202. " \"First Name\" : \"Bob\",\n"
  203. " \"Last Name\" : \"Smith\",\n"
  204. " \"Email\" : \"bsmith@someurl.com\",\n"
  205. " \"Phone\" : \"(555) 555-1212\"\n"
  206. " },\n"
  207. " {\n"
  208. " \"First Name\" : \"Jan\",\n"
  209. " \"Last Name\" : \"Smith\",\n"
  210. " \"Email\" : \"jsmith@someurl.com\",\n"
  211. " \"Phone\" : \"(555) 555-3434\"\n"
  212. " },\n"
  213. " {\n"
  214. " \"First Name\" : \"Sue\",\n"
  215. " \"Last Name\" : \"Smith\",\n"
  216. " \"Email\" : \"ssmith@someurl.com\",\n"
  217. " \"Phone\" : \"(555) 555-5656\"\n"
  218. " }\n"
  219. " ]\n"
  220. "}\n";
  221. const char *ok_data_12 =
  222. "{\" \\\" \\\\ \\b \\f \\n \\r \\t \" : \"\"}";
  223. const char *error_data_1 =
  224. ""; // No root object
  225. const char *error_data_2 =
  226. "{\n\"a\":1\n"; // Unclosed object brace
  227. const char *error_data_3 =
  228. "{\n\"a\":\n[1,2,3,4\n}"; // Unclosed array brace
  229. const char *error_data_4 =
  230. "{\n\"a\"\n}"; // No object
  231. const char *bug_data_pr4387 =
  232. "[1, 2, 3]"; // Root array
  233. const char *bug_data_pr7180_1 =
  234. "{\n"
  235. " \"a\": [\n"
  236. " \"1\",\n"
  237. " \"2\"\n"
  238. " ]\n"
  239. "}\n";
  240. const char *bug_data_pr7180_2 =
  241. "{\"a\":[\"1\",\"2\"]}\n";
  242. struct ReadFunc
  243. {
  244. template<class Ptree>
  245. void operator()(const std::string &filename, Ptree &pt) const
  246. {
  247. boost::property_tree::read_json(filename, pt);
  248. }
  249. template <class Ptree>
  250. void operator()(
  251. std::basic_istream<typename Ptree::key_type::value_type>& is,
  252. Ptree& pt) const
  253. {
  254. boost::property_tree::read_json(is, pt);
  255. }
  256. };
  257. struct WriteFunc
  258. {
  259. WriteFunc(bool pretty = false) : pretty(pretty) {}
  260. template<class Ptree>
  261. void operator()(const std::string &filename, const Ptree &pt) const
  262. {
  263. boost::property_tree::write_json(filename, pt, std::locale(), pretty);
  264. }
  265. template<class Ptree>
  266. void operator()(
  267. std::basic_ostream<typename Ptree::key_type::value_type>& os,
  268. Ptree& pt) const
  269. {
  270. boost::property_tree::write_json(os, pt, pretty);
  271. }
  272. bool pretty;
  273. };
  274. template<class Ptree>
  275. void test_json_parser()
  276. {
  277. using namespace boost::property_tree;
  278. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  279. (
  280. ReadFunc(), WriteFunc(false), ok_data_1, NULL,
  281. "testok1.json", NULL, "testok1out.json", 1, 0, 0
  282. );
  283. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  284. (
  285. ReadFunc(), WriteFunc(false), ok_data_2, NULL,
  286. "testok2.json", NULL, "testok2out.json", 18, 50, 74
  287. );
  288. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  289. (
  290. ReadFunc(), WriteFunc(false), ok_data_3, NULL,
  291. "testok3.json", NULL, "testok3out.json", 3, 1, 2
  292. );
  293. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  294. (
  295. ReadFunc(), WriteFunc(false), ok_data_4, NULL,
  296. "testok4.json", NULL, "testok4out.json", 11, 10, 4
  297. );
  298. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  299. (
  300. ReadFunc(), WriteFunc(false), ok_data_6, NULL,
  301. "testok6.json", NULL, "testok6out.json", 56, 265, 111
  302. );
  303. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  304. (
  305. ReadFunc(), WriteFunc(false), ok_data_7, NULL,
  306. "testok7.json", NULL, "testok7out.json", 87, 1046, 1216
  307. );
  308. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  309. (
  310. ReadFunc(), WriteFunc(false), ok_data_8, NULL,
  311. "testok8.json", NULL, "testok8out.json", 23, 149, 125
  312. );
  313. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  314. (
  315. ReadFunc(), WriteFunc(false), ok_data_9, NULL,
  316. "testok9.json", NULL, "testok9out.json", 15, 54, 60
  317. );
  318. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  319. (
  320. ReadFunc(), WriteFunc(false), ok_data_10, NULL,
  321. "testok10.json", NULL, "testok10out.json", 17, 162, 85
  322. );
  323. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  324. (
  325. ReadFunc(), WriteFunc(false), ok_data_11, NULL,
  326. "testok11.json", NULL, "testok11out.json", 17, 120, 91
  327. );
  328. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  329. (
  330. ReadFunc(), WriteFunc(false), ok_data_12, NULL,
  331. "testok12.json", NULL, "testok12out.json", 2, 0, 17
  332. );
  333. generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error>
  334. (
  335. ReadFunc(), WriteFunc(false), error_data_1, NULL,
  336. "testerr1.json", NULL, "testerr1out.json", 1
  337. );
  338. generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error>
  339. (
  340. ReadFunc(), WriteFunc(false), error_data_2, NULL,
  341. "testerr2.json", NULL, "testerr2out.json", 3
  342. );
  343. generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error>
  344. (
  345. ReadFunc(), WriteFunc(false), error_data_3, NULL,
  346. "testerr3.json", NULL, "testerr3out.json", 4
  347. );
  348. generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error>
  349. (
  350. ReadFunc(), WriteFunc(false), error_data_4, NULL,
  351. "testerr4.json", NULL, "testerr4out.json", 3
  352. );
  353. generic_parser_test_ok<Ptree, ReadFunc, WriteFunc>
  354. (
  355. ReadFunc(), WriteFunc(false), bug_data_pr4387, NULL,
  356. "testpr4387.json", NULL, "testpr4387out.json", 4, 3, 0
  357. );
  358. check_exact_roundtrip<Ptree>(ReadFunc(), WriteFunc(true),
  359. bug_data_pr7180_1);
  360. check_exact_roundtrip<Ptree>(ReadFunc(), WriteFunc(false),
  361. bug_data_pr7180_2);
  362. }
  363. void test_escaping_utf8()
  364. {
  365. // This is cyrillic text encoded as UTF-8
  366. std::string str = "\xD0\x9C\xD0\xB0\xD0\xBC\xD0\xB0 "
  367. "\xD0\xBC\xD1\x8B\xD0\xBB\xD0\xB0 \xD1\x80\xD0\xB0\xD0\xBC\xD1\x83";
  368. // Should NOT escape UTF-8
  369. BOOST_CHECK(boost::property_tree::json_parser::create_escapes(str) == str);
  370. }
  371. void test_escaping_wide()
  372. {
  373. // Should NOT escape characters within ASCII range.
  374. std::wstring str1 = L"I am wstring with ASCII";
  375. BOOST_CHECK(boost::property_tree::json_parser::create_escapes(str1) == str1);
  376. // Should escape characters outside ASCII range - this is NOT utf-8
  377. // This is cyrillic text
  378. std::wstring str2 = L"\u041C\u0430\u043C\u0430 "
  379. L"\u043C\u044B\u043B\u0430 \u0440\u0430\u043C\u0443";
  380. BOOST_CHECK(boost::property_tree::json_parser::create_escapes(str2) ==
  381. L"\\u041C\\u0430\\u043C\\u0430 "
  382. L"\\u043C\\u044B\\u043B\\u0430 \\u0440\\u0430\\u043C\\u0443");
  383. }
  384. int test_main(int argc, char *argv[])
  385. {
  386. using namespace boost::property_tree;
  387. test_json_parser<ptree>();
  388. test_json_parser<iptree>();
  389. test_escaping_utf8();
  390. #ifndef BOOST_NO_CWCHAR
  391. test_json_parser<wptree>();
  392. test_json_parser<wiptree>();
  393. test_escaping_wide();
  394. #endif
  395. return 0;
  396. }