blob: cb9db66cd13d3631d37c26adcba6c7b6b021a0b3 [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 Donahuedc4a7f92019-01-17 11:07:53 -050017#include <cstdio>
Billy Donahueb5e1fe82018-05-20 16:55:27 -040018#include <json/json.h>
Christopher Dunn2160c9a2015-01-23 09:02:44 -060019#include <sstream>
Christopher Dunnf9864232007-06-14 21:01:26 +000020
Billy Donahueb5e1fe82018-05-20 16:55:27 -040021struct Options {
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050022 Json::String path;
Christopher Dunn3682f602015-01-23 11:46:05 -060023 Json::Features features;
24 bool parseOnly;
Billy Donahue2b593a92019-01-18 03:46:57 -050025 using writeFuncType = Json::String (*)(Json::Value const&);
Christopher Dunn3682f602015-01-23 11:46:05 -060026 writeFuncType write;
27};
28
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050029static Json::String normalizeFloatingPointStr(double value) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100030 char buffer[32];
Hans Johnson5c8e5392018-12-12 13:31:55 -060031 jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100032 buffer[sizeof(buffer) - 1] = 0;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050033 Json::String s(buffer);
34 Json::String::size_type index = s.find_last_of("eE");
35 if (index != Json::String::npos) {
36 Json::String::size_type hasSign =
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100037 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050038 Json::String::size_type exponentStartIndex = index + 1 + hasSign;
39 Json::String normalized = s.substr(0, exponentStartIndex);
Billy Donahue2b593a92019-01-18 03:46:57 -050040 Json::String::size_type indexDigit =
41 s.find_first_not_of('0', exponentStartIndex);
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050042 Json::String exponent = "0";
43 if (indexDigit != Json::String::npos) // There is an exponent different
Billy Donahue2b593a92019-01-18 03:46:57 -050044 // from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000045 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100046 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000047 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100048 return normalized + exponent;
49 }
50 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000051}
52
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050053static Json::String readInputTestFile(const char* path) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +100054 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100055 if (!file)
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050056 return "";
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100057 fseek(file, 0, SEEK_END);
Christopher Dunnd4513fc2016-02-06 09:25:20 -060058 long const size = ftell(file);
Hans Johnson1fc3de72019-01-14 17:09:15 -060059 size_t const usize = static_cast<unsigned long>(size);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100060 fseek(file, 0, SEEK_SET);
Aaron Jacobs11086dd2014-09-15 10:15:29 +100061 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100062 buffer[size] = 0;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -050063 Json::String text;
Christopher Dunnd4513fc2016-02-06 09:25:20 -060064 if (fread(buffer, 1, usize, file) == usize)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100065 text = buffer;
66 fclose(file);
67 delete[] buffer;
68 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000069}
70
Jordan Baylesf34bf242019-10-11 11:19:00 -070071static void printValueTree(FILE* fout, Json::Value& value,
72 const Json::String& path = ".") {
Cory Quammen4d234922014-10-09 16:29:47 -040073 if (value.hasComment(Json::commentBefore)) {
74 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
75 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100076 switch (value.type()) {
77 case Json::nullValue:
78 fprintf(fout, "%s=null\n", path.c_str());
79 break;
80 case Json::intValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040081 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100082 Json::valueToString(value.asLargestInt()).c_str());
83 break;
84 case Json::uintValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040085 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100086 Json::valueToString(value.asLargestUInt()).c_str());
87 break;
88 case Json::realValue:
Billy Donahueb5e1fe82018-05-20 16:55:27 -040089 fprintf(fout, "%s=%s\n", path.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100090 normalizeFloatingPointStr(value.asDouble()).c_str());
91 break;
92 case Json::stringValue:
93 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
94 break;
95 case Json::booleanValue:
96 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
97 break;
98 case Json::arrayValue: {
99 fprintf(fout, "%s=[]\n", path.c_str());
Christopher Dunnd4513fc2016-02-06 09:25:20 -0600100 Json::ArrayIndex size = value.size();
101 for (Json::ArrayIndex index = 0; index < size; ++index) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000102 static char buffer[16];
Hans Johnson5c8e5392018-12-12 13:31:55 -0600103 jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000104 printValueTree(fout, value[index], path + buffer);
105 }
106 } break;
107 case Json::objectValue: {
108 fprintf(fout, "%s={}\n", path.c_str());
109 Json::Value::Members members(value.getMemberNames());
110 std::sort(members.begin(), members.end());
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500111 Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
Hans Johnsoncbeed7b2019-01-14 17:09:12 -0600112 for (auto name : members) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000113 printValueTree(fout, value[name], path + suffix + name);
114 }
115 } break;
116 default:
117 break;
118 }
Cory Quammen4d234922014-10-09 16:29:47 -0400119
120 if (value.hasComment(Json::commentAfter)) {
121 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
122 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000123}
124
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500125static int parseAndSaveValueTree(const Json::String& input,
126 const Json::String& actual,
127 const Json::String& kind,
Jordan Baylesf34bf242019-10-11 11:19:00 -0700128 const Json::Features& features, bool parseOnly,
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400129 Json::Value* root) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000130 Json::Reader reader(features);
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400131 bool parsingSuccessful =
132 reader.parse(input.data(), input.data() + input.size(), *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000133 if (!parsingSuccessful) {
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400134 printf("Failed to parse %s file: \n%s\n", kind.c_str(),
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000135 reader.getFormattedErrorMessages().c_str());
136 return 1;
137 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000138 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000139 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000140 if (!factual) {
141 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000142 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000143 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600144 printValueTree(factual, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000145 fclose(factual);
146 }
147 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000148}
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500149// static Json::String useFastWriter(Json::Value const& root) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600150// Json::FastWriter writer;
151// writer.enableYAMLCompatibility();
152// return writer.write(root);
153// }
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500154static Json::String useStyledWriter(Json::Value const& root) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600155 Json::StyledWriter writer;
156 return writer.write(root);
157}
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500158static Json::String useStyledStreamWriter(Json::Value const& root) {
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600159 Json::StyledStreamWriter writer;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500160 Json::OStringStream sout;
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600161 writer.write(sout, root);
Christopher Dunn79211e12015-01-23 11:27:19 -0600162 return sout.str();
163}
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500164static Json::String useBuiltStyledStreamWriter(Json::Value const& root) {
Christopher Dunn6065a1c2015-01-26 11:01:15 -0600165 Json::StreamWriterBuilder builder;
Christopher Dunn694dbcb2015-02-09 15:25:57 -0600166 return Json::writeString(builder, root);
Christopher Dunn9243d602015-01-23 08:38:32 -0600167}
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500168static int rewriteValueTree(const Json::String& rewritePath,
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400169 const Json::Value& root,
170 Options::writeFuncType write,
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500171 Json::String* rewrite) {
Christopher Dunn3682f602015-01-23 11:46:05 -0600172 *rewrite = write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000173 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000174 if (!fout) {
175 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
176 return 2;
177 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600178 fprintf(fout, "%s\n", rewrite->c_str());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000179 fclose(fout);
180 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000181}
182
Billy Donahue2b593a92019-01-18 03:46:57 -0500183static Json::String removeSuffix(const Json::String& path,
184 const Json::String& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000185 if (extension.length() >= path.length())
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500186 return Json::String("");
187 Json::String suffix = path.substr(path.length() - extension.length());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000188 if (suffix != extension)
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500189 return Json::String("");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000190 return path.substr(0, path.length() - extension.length());
191}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000192
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000193static void printConfig() {
194// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000195#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000196 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000197#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000198 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000199#endif
200}
201
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000202static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000203 printf("Usage: %s [--strict] input-json-file", argv[0]);
204 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000205}
206
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400207static int parseCommandLine(int argc, const char* argv[], Options* opts) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600208 opts->parseOnly = false;
Christopher Dunn3682f602015-01-23 11:46:05 -0600209 opts->write = &useStyledWriter;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000210 if (argc < 2) {
211 return printUsage(argv);
212 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000213 int index = 1;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500214 if (Json::String(argv[index]) == "--json-checker") {
Christopher Dunn79211e12015-01-23 11:27:19 -0600215 opts->features = Json::Features::strictMode();
216 opts->parseOnly = true;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000217 ++index;
218 }
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500219 if (Json::String(argv[index]) == "--json-config") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000220 printConfig();
221 return 3;
222 }
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500223 if (Json::String(argv[index]) == "--json-writer") {
Christopher Dunn3682f602015-01-23 11:46:05 -0600224 ++index;
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500225 Json::String const writerName(argv[index++]);
Christopher Dunn3682f602015-01-23 11:46:05 -0600226 if (writerName == "StyledWriter") {
227 opts->write = &useStyledWriter;
228 } else if (writerName == "StyledStreamWriter") {
229 opts->write = &useStyledStreamWriter;
Christopher Dunn9243d602015-01-23 08:38:32 -0600230 } else if (writerName == "BuiltStyledStreamWriter") {
231 opts->write = &useBuiltStyledStreamWriter;
Christopher Dunn3682f602015-01-23 11:46:05 -0600232 } else {
233 printf("Unknown '--json-writer %s'\n", writerName.c_str());
234 return 4;
235 }
236 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000237 if (index == argc || index + 1 < argc) {
238 return printUsage(argv);
239 }
Christopher Dunn79211e12015-01-23 11:27:19 -0600240 opts->path = argv[index];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000241 return 0;
242}
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400243static int runTest(Options const& opts) {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600244 int exitCode = 0;
245
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500246 Json::String input = readInputTestFile(opts.path.c_str());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600247 if (input.empty()) {
248 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
249 return 3;
250 }
251
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500252 Json::String basePath = removeSuffix(opts.path, ".json");
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600253 if (!opts.parseOnly && basePath.empty()) {
254 printf("Bad input path. Path does not end with '.expected':\n%s\n",
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400255 opts.path.c_str());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600256 return 3;
257 }
258
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500259 Json::String const actualPath = basePath + ".actual";
260 Json::String const rewritePath = basePath + ".rewrite";
261 Json::String const rewriteActualPath = basePath + ".actual-rewrite";
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600262
263 Json::Value root;
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400264 exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
265 opts.parseOnly, &root);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600266 if (exitCode || opts.parseOnly) {
267 return exitCode;
268 }
Billy Donahue1c2ed7a2019-01-17 16:35:29 -0500269 Json::String rewrite;
Christopher Dunn3682f602015-01-23 11:46:05 -0600270 exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600271 if (exitCode) {
272 return exitCode;
273 }
274 Json::Value rewriteRoot;
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400275 exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
276 opts.features, opts.parseOnly, &rewriteRoot);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600277 if (exitCode) {
278 return exitCode;
279 }
280 return 0;
Christopher Dunn79211e12015-01-23 11:27:19 -0600281}
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000282int main(int argc, const char* argv[]) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600283 Options opts;
Gaurav6c145482015-09-22 13:53:19 +0530284 try {
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400285 int exitCode = parseCommandLine(argc, argv, &opts);
286 if (exitCode != 0) {
287 printf("Failed to parse command-line.");
288 return exitCode;
289 }
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600290 return runTest(opts);
Billy Donahueb5e1fe82018-05-20 16:55:27 -0400291 } catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000292 printf("Unhandled exception:\n%s\n", e.what());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600293 return 1;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000294 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000295}
Christopher Dunn90591c72017-08-28 08:38:29 -0500296
Marcel Raad240c85a2017-11-10 10:58:43 +0100297#if defined(__GNUC__)
Christopher Dunn90591c72017-08-28 08:38:29 -0500298#pragma GCC diagnostic pop
Marcel Raad240c85a2017-11-10 10:58:43 +0100299#endif