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 | |
17 | const char *ok_data_1 = |
18 | "{}" ; |
19 | |
20 | const 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 | |
36 | const char *ok_data_3 = |
37 | "{\"a\":{\"b\":\"c\"}}" ; |
38 | |
39 | const char *ok_data_4 = |
40 | "{\"a\":[{\"b\":\"c\"},{\"d\":\"e\"},1,2,3,4],\"f\":null}" ; |
41 | |
42 | const 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 | |
71 | const 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 | |
161 | const 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 | |
177 | const 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 | |
190 | const 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 | |
210 | const 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 | |
234 | const char *ok_data_12 = |
235 | "{\" \\\" \\\\ \\b \\f \\n \\r \\t \" : \"\"}" ; |
236 | |
237 | const char *error_data_1 = |
238 | "" ; // No root object |
239 | |
240 | const char *error_data_2 = |
241 | "{\n\"a\":1\n" ; // Unclosed object brace |
242 | |
243 | const char *error_data_3 = |
244 | "{\n\"a\":\n[1,2,3,4\n}" ; // Unclosed array brace |
245 | |
246 | const char *error_data_4 = |
247 | "{\n\"a\"\n}" ; // No object |
248 | |
249 | const char *bug_data_pr4387 = |
250 | "[1, 2, 3]" ; // Root array |
251 | |
252 | const char *bug_data_pr7180_1 = |
253 | "{\n" |
254 | " \"a\": [\n" |
255 | " \"1\",\n" |
256 | " \"2\"\n" |
257 | " ]\n" |
258 | "}\n" ; |
259 | const char *bug_data_pr7180_2 = |
260 | "{\"a\":[\"1\",\"2\"]}" ; |
261 | |
262 | struct 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 | |
278 | struct 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 | |
299 | template<class Ptree> |
300 | void 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 | |
408 | void 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 | |
417 | void 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 | |
431 | int 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 | |