blob: 54ca302961535a4e294fb6c8ebf2cbf0091f689b [file] [log] [blame]
Devin Jeanpierre59e4d352017-07-21 03:44:36 -07001// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
Baptiste Lepilleur7469f1d2010-04-20 21:35:19 +00002// Distributed under MIT license, or public domain if desired and
3// recognized in your jurisdiction.
4// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5
Marcel Raad240c85a2017-11-10 10:58:43 +01006#if defined(__GNUC__)
Christopher Dunn90591c72017-08-28 08:38:29 -05007#pragma GCC diagnostic push
8#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Marcel Raad240c85a2017-11-10 10:58:43 +01009#elif defined(_MSC_VER)
10#pragma warning(disable : 4996)
11#endif
Christopher Dunn90591c72017-08-28 08:38:29 -050012
Baptiste Lepilleurfa130ef2010-12-24 12:47:14 +000013/* This executable is used for testing parser/writer using real JSON files.
14 */
15
Christopher Dunnf9864232007-06-14 21:01:26 +000016#include <algorithm> // sort
Billy Donahueb5e1fe82018-05-20 16:55:27 -040017#include <json/json.h>
Christopher Dunn2160c9a2015-01-23 09:02:44 -060018#include <sstream>
Christopher Dunnf9864232007-06-14 21:01:26 +000019#include <stdio.h>
20
Billy Donahueb5e1fe82018-05-20 16:55:27 -040021struct Options {
Christopher Dawes75570d72016-03-07 08:29:59 +000022 JSONCPP_STRING path;
Christopher Dunn3682f602015-01-23 11:46:05 -060023 Json::Features features;
24 bool parseOnly;
Christopher Dawes75570d72016-03-07 08:29:59 +000025 typedef JSONCPP_STRING (*writeFuncType)(Json::Value const&);
Christopher Dunn3682f602015-01-23 11:46:05 -060026 writeFuncType write;
27};
28
Christopher Dawes75570d72016-03-07 08:29:59 +000029static JSONCPP_STRING normalizeFloatingPointStr(double value) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100030 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000031#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100032 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000033#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100034 snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000035#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100036 buffer[sizeof(buffer) - 1] = 0;
Christopher Dawes75570d72016-03-07 08:29:59 +000037 JSONCPP_STRING s(buffer);
Christopher Dawes75570d72016-03-07 08:29:59 +000038 JSONCPP_STRING::size_type index = s.find_last_of("eE");
39 if (index != JSONCPP_STRING::npos) {
40 JSONCPP_STRING::size_type hasSign =
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100041 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
Christopher Dawes75570d72016-03-07 08:29:59 +000042 JSONCPP_STRING::size_type exponentStartIndex = index + 1 + hasSign;
43 JSONCPP_STRING normalized = s.substr(0, exponentStartIndex);
44 JSONCPP_STRING::size_type indexDigit =
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100045 s.find_first_not_of('0', exponentStartIndex);
Christopher Dawes75570d72016-03-07 08:29:59 +000046 JSONCPP_STRING exponent = "0";
Billy Donahueb5e1fe82018-05-20 16:55:27 -040047 if (indexDigit != JSONCPP_STRING::npos) // There is an exponent different
48 // from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000049 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100050 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000051 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100052 return normalized + exponent;
53 }
54 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000055}
56
Christopher Dawes75570d72016-03-07 08:29:59 +000057static JSONCPP_STRING readInputTestFile(const char* path) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +100058 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100059 if (!file)
Christopher Dawes75570d72016-03-07 08:29:59 +000060 return JSONCPP_STRING("");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100061 fseek(file, 0, SEEK_END);
Christopher Dunnd4513fc2016-02-06 09:25:20 -060062 long const size = ftell(file);
Christopher Dunnb9996162016-05-15 23:13:47 -050063 unsigned long const usize = static_cast<unsigned long>(size);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100064 fseek(file, 0, SEEK_SET);
Christopher Dawes75570d72016-03-07 08:29:59 +000065 JSONCPP_STRING text;
Aaron Jacobs11086dd2014-09-15 10:15:29 +100066 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100067 buffer[size] = 0;
Christopher Dunnd4513fc2016-02-06 09:25:20 -060068 if (fread(buffer, 1, usize, file) == usize)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100069 text = buffer;
70 fclose(file);
71 delete[] buffer;
72 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000073}
74
Billy Donahueb5e1fe82018-05-20 16:55:27 -040075static void printValueTree(FILE* fout,
76 Json::Value& value,
77 const JSONCPP_STRING& path = ".") {
Cory Quammen4d234922014-10-09 16:29:47 -040078 if (value.hasComment(Json::commentBefore)) {
79 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
80 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100081 switch (value.type()) {
82 case Json::nullValue:
83 fprintf(fout, "%s=null\n", path.c_str());
84 break;
85 case Json::intValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040086 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100087 Json::valueToString(value.asLargestInt()).c_str());
88 break;
89 case Json::uintValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040090 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100091 Json::valueToString(value.asLargestUInt()).c_str());
92 break;
93 case Json::realValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040094 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100095 normalizeFloatingPointStr(value.asDouble()).c_str());
96 break;
97 case Json::stringValue:
98 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
99 break;
100 case Json::booleanValue:
101 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
102 break;
103 case Json::arrayValue: {
104 fprintf(fout, "%s=[]\n", path.c_str());
Christopher Dunnd4513fc2016-02-06 09:25:20 -0600105 Json::ArrayIndex size = value.size();
106 for (Json::ArrayIndex index = 0; index < size; ++index) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000107 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +0000108#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Marian Klymov85a263e2018-06-02 19:38:12 +0300109 sprintf_s(buffer, sizeof(buffer), "[%u]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000110#else
Marian Klymov85a263e2018-06-02 19:38:12 +0300111 snprintf(buffer, sizeof(buffer), "[%u]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000112#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000113 printValueTree(fout, value[index], path + buffer);
114 }
115 } break;
116 case Json::objectValue: {
117 fprintf(fout, "%s={}\n", path.c_str());
118 Json::Value::Members members(value.getMemberNames());
119 std::sort(members.begin(), members.end());
Christopher Dawes75570d72016-03-07 08:29:59 +0000120 JSONCPP_STRING suffix = *(path.end() - 1) == '.' ? "" : ".";
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000121 for (Json::Value::Members::iterator it = members.begin();
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400122 it != members.end(); ++it) {
Christopher Dawes75570d72016-03-07 08:29:59 +0000123 const JSONCPP_STRING name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000124 printValueTree(fout, value[name], path + suffix + name);
125 }
126 } break;
127 default:
128 break;
129 }
Cory Quammen4d234922014-10-09 16:29:47 -0400130
131 if (value.hasComment(Json::commentAfter)) {
132 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
133 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000134}
135
Christopher Dawes75570d72016-03-07 08:29:59 +0000136static int parseAndSaveValueTree(const JSONCPP_STRING& input,
137 const JSONCPP_STRING& actual,
138 const JSONCPP_STRING& kind,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000139 const Json::Features& features,
Christopher Dunn632c9b52015-01-23 11:09:04 -0600140 bool parseOnly,
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400141 Json::Value* root) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000142 Json::Reader reader(features);
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400143 bool parsingSuccessful =
144 reader.parse(input.data(), input.data() + input.size(), *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000145 if (!parsingSuccessful) {
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400146 printf("Failed to parse %s file: \n%s\n", kind.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000147 reader.getFormattedErrorMessages().c_str());
148 return 1;
149 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000150 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000151 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000152 if (!factual) {
153 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000154 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000155 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600156 printValueTree(factual, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000157 fclose(factual);
158 }
159 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000160}
Christopher Dawes75570d72016-03-07 08:29:59 +0000161// static JSONCPP_STRING useFastWriter(Json::Value const& root) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600162// Json::FastWriter writer;
163// writer.enableYAMLCompatibility();
164// return writer.write(root);
165// }
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400166static JSONCPP_STRING useStyledWriter(Json::Value const& root) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600167 Json::StyledWriter writer;
168 return writer.write(root);
169}
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400170static JSONCPP_STRING useStyledStreamWriter(Json::Value const& root) {
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600171 Json::StyledStreamWriter writer;
Christopher Dunn38bb4912016-03-06 11:50:00 -0600172 JSONCPP_OSTRINGSTREAM sout;
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600173 writer.write(sout, root);
Christopher Dunn79211e12015-01-23 11:27:19 -0600174 return sout.str();
175}
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400176static JSONCPP_STRING useBuiltStyledStreamWriter(Json::Value const& root) {
Christopher Dunn6065a1c2015-01-26 11:01:15 -0600177 Json::StreamWriterBuilder builder;
Christopher Dunn694dbcb2015-02-09 15:25:57 -0600178 return Json::writeString(builder, root);
Christopher Dunn9243d602015-01-23 08:38:32 -0600179}
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400180static int rewriteValueTree(const JSONCPP_STRING& rewritePath,
181 const Json::Value& root,
182 Options::writeFuncType write,
183 JSONCPP_STRING* rewrite) {
Christopher Dunn3682f602015-01-23 11:46:05 -0600184 *rewrite = write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000185 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000186 if (!fout) {
187 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
188 return 2;
189 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600190 fprintf(fout, "%s\n", rewrite->c_str());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000191 fclose(fout);
192 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000193}
194
Christopher Dawes75570d72016-03-07 08:29:59 +0000195static JSONCPP_STRING removeSuffix(const JSONCPP_STRING& path,
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400196 const JSONCPP_STRING& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000197 if (extension.length() >= path.length())
Christopher Dawes75570d72016-03-07 08:29:59 +0000198 return JSONCPP_STRING("");
199 JSONCPP_STRING suffix = path.substr(path.length() - extension.length());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000200 if (suffix != extension)
Christopher Dawes75570d72016-03-07 08:29:59 +0000201 return JSONCPP_STRING("");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000202 return path.substr(0, path.length() - extension.length());
203}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000204
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000205static void printConfig() {
206// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000207#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000208 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000209#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000210 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000211#endif
212}
213
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000214static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000215 printf("Usage: %s [--strict] input-json-file", argv[0]);
216 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000217}
218
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400219static int parseCommandLine(int argc, const char* argv[], Options* opts) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600220 opts->parseOnly = false;
Christopher Dunn3682f602015-01-23 11:46:05 -0600221 opts->write = &useStyledWriter;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000222 if (argc < 2) {
223 return printUsage(argv);
224 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000225 int index = 1;
Christopher Dawes75570d72016-03-07 08:29:59 +0000226 if (JSONCPP_STRING(argv[index]) == "--json-checker") {
Christopher Dunn79211e12015-01-23 11:27:19 -0600227 opts->features = Json::Features::strictMode();
228 opts->parseOnly = true;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000229 ++index;
230 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000231 if (JSONCPP_STRING(argv[index]) == "--json-config") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000232 printConfig();
233 return 3;
234 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000235 if (JSONCPP_STRING(argv[index]) == "--json-writer") {
Christopher Dunn3682f602015-01-23 11:46:05 -0600236 ++index;
Christopher Dawes75570d72016-03-07 08:29:59 +0000237 JSONCPP_STRING const writerName(argv[index++]);
Christopher Dunn3682f602015-01-23 11:46:05 -0600238 if (writerName == "StyledWriter") {
239 opts->write = &useStyledWriter;
240 } else if (writerName == "StyledStreamWriter") {
241 opts->write = &useStyledStreamWriter;
Christopher Dunn9243d602015-01-23 08:38:32 -0600242 } else if (writerName == "BuiltStyledStreamWriter") {
243 opts->write = &useBuiltStyledStreamWriter;
Christopher Dunn3682f602015-01-23 11:46:05 -0600244 } else {
245 printf("Unknown '--json-writer %s'\n", writerName.c_str());
246 return 4;
247 }
248 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000249 if (index == argc || index + 1 < argc) {
250 return printUsage(argv);
251 }
Christopher Dunn79211e12015-01-23 11:27:19 -0600252 opts->path = argv[index];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000253 return 0;
254}
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400255static int runTest(Options const& opts) {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600256 int exitCode = 0;
257
Christopher Dawes75570d72016-03-07 08:29:59 +0000258 JSONCPP_STRING input = readInputTestFile(opts.path.c_str());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600259 if (input.empty()) {
260 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
261 return 3;
262 }
263
Christopher Dawes75570d72016-03-07 08:29:59 +0000264 JSONCPP_STRING basePath = removeSuffix(opts.path, ".json");
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600265 if (!opts.parseOnly && basePath.empty()) {
266 printf("Bad input path. Path does not end with '.expected':\n%s\n",
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400267 opts.path.c_str());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600268 return 3;
269 }
270
Christopher Dawes75570d72016-03-07 08:29:59 +0000271 JSONCPP_STRING const actualPath = basePath + ".actual";
272 JSONCPP_STRING const rewritePath = basePath + ".rewrite";
273 JSONCPP_STRING const rewriteActualPath = basePath + ".actual-rewrite";
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600274
275 Json::Value root;
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400276 exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
277 opts.parseOnly, &root);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600278 if (exitCode || opts.parseOnly) {
279 return exitCode;
280 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000281 JSONCPP_STRING rewrite;
Christopher Dunn3682f602015-01-23 11:46:05 -0600282 exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600283 if (exitCode) {
284 return exitCode;
285 }
286 Json::Value rewriteRoot;
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400287 exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
288 opts.features, opts.parseOnly, &rewriteRoot);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600289 if (exitCode) {
290 return exitCode;
291 }
292 return 0;
Christopher Dunn79211e12015-01-23 11:27:19 -0600293}
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000294int main(int argc, const char* argv[]) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600295 Options opts;
Gaurav6c145482015-09-22 13:53:19 +0530296 try {
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400297 int exitCode = parseCommandLine(argc, argv, &opts);
298 if (exitCode != 0) {
299 printf("Failed to parse command-line.");
300 return exitCode;
301 }
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600302 return runTest(opts);
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400303 } catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000304 printf("Unhandled exception:\n%s\n", e.what());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600305 return 1;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000306 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000307}
Christopher Dunn90591c72017-08-28 08:38:29 -0500308
Marcel Raad240c85a2017-11-10 10:58:43 +0100309#if defined(__GNUC__)
Christopher Dunn90591c72017-08-28 08:38:29 -0500310#pragma GCC diagnostic pop
Marcel Raad240c85a2017-11-10 10:58:43 +0100311#endif