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

source code of boost/libs/property_tree/test/test_json_parser.cpp