blob: d24d7cfb71126ad70d611a8b31e433fdfaba0be9 [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
Christopher Dunn90591c72017-08-28 08:38:29 -05006#pragma GCC diagnostic push
7#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
8
Baptiste Lepilleurfa130ef2010-12-24 12:47:14 +00009/* This executable is used for testing parser/writer using real JSON files.
10 */
11
Christopher Dunnf9864232007-06-14 21:01:26 +000012#include <json/json.h>
13#include <algorithm> // sort
Christopher Dunn2160c9a2015-01-23 09:02:44 -060014#include <sstream>
Christopher Dunnf9864232007-06-14 21:01:26 +000015#include <stdio.h>
16
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100017#if defined(_MSC_VER) && _MSC_VER >= 1310
18#pragma warning(disable : 4996) // disable fopen deprecation warning
Christopher Dunnf9864232007-06-14 21:01:26 +000019#endif
20
Christopher Dunn3682f602015-01-23 11:46:05 -060021struct Options
22{
Christopher Dawes75570d72016-03-07 08:29:59 +000023 JSONCPP_STRING path;
Christopher Dunn3682f602015-01-23 11:46:05 -060024 Json::Features features;
25 bool parseOnly;
Christopher Dawes75570d72016-03-07 08:29:59 +000026 typedef JSONCPP_STRING (*writeFuncType)(Json::Value const&);
Christopher Dunn3682f602015-01-23 11:46:05 -060027 writeFuncType write;
28};
29
Christopher Dawes75570d72016-03-07 08:29:59 +000030static JSONCPP_STRING normalizeFloatingPointStr(double value) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100031 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000032#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100033 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000034#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100035 snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000036#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100037 buffer[sizeof(buffer) - 1] = 0;
Christopher Dawes75570d72016-03-07 08:29:59 +000038 JSONCPP_STRING s(buffer);
Christopher Dawes75570d72016-03-07 08:29:59 +000039 JSONCPP_STRING::size_type index = s.find_last_of("eE");
40 if (index != JSONCPP_STRING::npos) {
41 JSONCPP_STRING::size_type hasSign =
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100042 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
Christopher Dawes75570d72016-03-07 08:29:59 +000043 JSONCPP_STRING::size_type exponentStartIndex = index + 1 + hasSign;
44 JSONCPP_STRING normalized = s.substr(0, exponentStartIndex);
45 JSONCPP_STRING::size_type indexDigit =
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100046 s.find_first_not_of('0', exponentStartIndex);
Christopher Dawes75570d72016-03-07 08:29:59 +000047 JSONCPP_STRING exponent = "0";
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100048 if (indexDigit !=
Christopher Dawes75570d72016-03-07 08:29:59 +000049 JSONCPP_STRING::npos) // There is an exponent different from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000050 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100051 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000052 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100053 return normalized + exponent;
54 }
55 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000056}
57
Christopher Dawes75570d72016-03-07 08:29:59 +000058static JSONCPP_STRING readInputTestFile(const char* path) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +100059 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100060 if (!file)
Christopher Dawes75570d72016-03-07 08:29:59 +000061 return JSONCPP_STRING("");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100062 fseek(file, 0, SEEK_END);
Christopher Dunnd4513fc2016-02-06 09:25:20 -060063 long const size = ftell(file);
Christopher Dunnb9996162016-05-15 23:13:47 -050064 unsigned long const usize = static_cast<unsigned long>(size);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100065 fseek(file, 0, SEEK_SET);
Christopher Dawes75570d72016-03-07 08:29:59 +000066 JSONCPP_STRING text;
Aaron Jacobs11086dd2014-09-15 10:15:29 +100067 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100068 buffer[size] = 0;
Christopher Dunnd4513fc2016-02-06 09:25:20 -060069 if (fread(buffer, 1, usize, file) == usize)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100070 text = buffer;
71 fclose(file);
72 delete[] buffer;
73 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000074}
75
Christopher Dunnf9864232007-06-14 21:01:26 +000076static void
Christopher Dawes75570d72016-03-07 08:29:59 +000077printValueTree(FILE* fout, Json::Value& value, 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:
86 fprintf(fout,
87 "%s=%s\n",
88 path.c_str(),
89 Json::valueToString(value.asLargestInt()).c_str());
90 break;
91 case Json::uintValue:
92 fprintf(fout,
93 "%s=%s\n",
94 path.c_str(),
95 Json::valueToString(value.asLargestUInt()).c_str());
96 break;
97 case Json::realValue:
98 fprintf(fout,
99 "%s=%s\n",
100 path.c_str(),
101 normalizeFloatingPointStr(value.asDouble()).c_str());
102 break;
103 case Json::stringValue:
104 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
105 break;
106 case Json::booleanValue:
107 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
108 break;
109 case Json::arrayValue: {
110 fprintf(fout, "%s=[]\n", path.c_str());
Christopher Dunnd4513fc2016-02-06 09:25:20 -0600111 Json::ArrayIndex size = value.size();
112 for (Json::ArrayIndex index = 0; index < size; ++index) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000113 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +0000114#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000115 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000116#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000117 snprintf(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000118#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000119 printValueTree(fout, value[index], path + buffer);
120 }
121 } break;
122 case Json::objectValue: {
123 fprintf(fout, "%s={}\n", path.c_str());
124 Json::Value::Members members(value.getMemberNames());
125 std::sort(members.begin(), members.end());
Christopher Dawes75570d72016-03-07 08:29:59 +0000126 JSONCPP_STRING suffix = *(path.end() - 1) == '.' ? "" : ".";
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000127 for (Json::Value::Members::iterator it = members.begin();
128 it != members.end();
129 ++it) {
Christopher Dawes75570d72016-03-07 08:29:59 +0000130 const JSONCPP_STRING name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000131 printValueTree(fout, value[name], path + suffix + name);
132 }
133 } break;
134 default:
135 break;
136 }
Cory Quammen4d234922014-10-09 16:29:47 -0400137
138 if (value.hasComment(Json::commentAfter)) {
139 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
140 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000141}
142
Christopher Dawes75570d72016-03-07 08:29:59 +0000143static int parseAndSaveValueTree(const JSONCPP_STRING& input,
144 const JSONCPP_STRING& actual,
145 const JSONCPP_STRING& kind,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000146 const Json::Features& features,
Christopher Dunn632c9b52015-01-23 11:09:04 -0600147 bool parseOnly,
Christopher Dunn79211e12015-01-23 11:27:19 -0600148 Json::Value* root)
149{
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000150 Json::Reader reader(features);
dawescae564652016-03-14 19:11:02 -0500151 bool parsingSuccessful = reader.parse(input.data(), input.data() + input.size(), *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000152 if (!parsingSuccessful) {
153 printf("Failed to parse %s file: \n%s\n",
154 kind.c_str(),
155 reader.getFormattedErrorMessages().c_str());
156 return 1;
157 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000158 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000159 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000160 if (!factual) {
161 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000162 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000163 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600164 printValueTree(factual, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000165 fclose(factual);
166 }
167 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000168}
Christopher Dawes75570d72016-03-07 08:29:59 +0000169// static JSONCPP_STRING useFastWriter(Json::Value const& root) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600170// Json::FastWriter writer;
171// writer.enableYAMLCompatibility();
172// return writer.write(root);
173// }
Christopher Dawes75570d72016-03-07 08:29:59 +0000174static JSONCPP_STRING useStyledWriter(
Christopher Dunn79211e12015-01-23 11:27:19 -0600175 Json::Value const& root)
176{
177 Json::StyledWriter writer;
178 return writer.write(root);
179}
Christopher Dawes75570d72016-03-07 08:29:59 +0000180static JSONCPP_STRING useStyledStreamWriter(
Christopher Dunn79211e12015-01-23 11:27:19 -0600181 Json::Value const& root)
182{
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600183 Json::StyledStreamWriter writer;
Christopher Dunn38bb4912016-03-06 11:50:00 -0600184 JSONCPP_OSTRINGSTREAM sout;
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600185 writer.write(sout, root);
Christopher Dunn79211e12015-01-23 11:27:19 -0600186 return sout.str();
187}
Christopher Dawes75570d72016-03-07 08:29:59 +0000188static JSONCPP_STRING useBuiltStyledStreamWriter(
Christopher Dunn9243d602015-01-23 08:38:32 -0600189 Json::Value const& root)
190{
Christopher Dunn6065a1c2015-01-26 11:01:15 -0600191 Json::StreamWriterBuilder builder;
Christopher Dunn694dbcb2015-02-09 15:25:57 -0600192 return Json::writeString(builder, root);
Christopher Dunn9243d602015-01-23 08:38:32 -0600193}
Christopher Dunn79211e12015-01-23 11:27:19 -0600194static int rewriteValueTree(
Christopher Dawes75570d72016-03-07 08:29:59 +0000195 const JSONCPP_STRING& rewritePath,
Christopher Dunn79211e12015-01-23 11:27:19 -0600196 const Json::Value& root,
Christopher Dunn3682f602015-01-23 11:46:05 -0600197 Options::writeFuncType write,
Christopher Dawes75570d72016-03-07 08:29:59 +0000198 JSONCPP_STRING* rewrite)
Christopher Dunn79211e12015-01-23 11:27:19 -0600199{
Christopher Dunn3682f602015-01-23 11:46:05 -0600200 *rewrite = write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000201 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000202 if (!fout) {
203 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
204 return 2;
205 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600206 fprintf(fout, "%s\n", rewrite->c_str());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000207 fclose(fout);
208 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000209}
210
Christopher Dawes75570d72016-03-07 08:29:59 +0000211static JSONCPP_STRING removeSuffix(const JSONCPP_STRING& path,
212 const JSONCPP_STRING& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000213 if (extension.length() >= path.length())
Christopher Dawes75570d72016-03-07 08:29:59 +0000214 return JSONCPP_STRING("");
215 JSONCPP_STRING suffix = path.substr(path.length() - extension.length());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000216 if (suffix != extension)
Christopher Dawes75570d72016-03-07 08:29:59 +0000217 return JSONCPP_STRING("");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000218 return path.substr(0, path.length() - extension.length());
219}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000220
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000221static void printConfig() {
222// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000223#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000224 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000225#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000226 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000227#endif
228}
229
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000230static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000231 printf("Usage: %s [--strict] input-json-file", argv[0]);
232 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000233}
234
Christopher Dunn79211e12015-01-23 11:27:19 -0600235static int parseCommandLine(
236 int argc, const char* argv[], Options* opts)
237{
238 opts->parseOnly = false;
Christopher Dunn3682f602015-01-23 11:46:05 -0600239 opts->write = &useStyledWriter;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000240 if (argc < 2) {
241 return printUsage(argv);
242 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000243 int index = 1;
Christopher Dawes75570d72016-03-07 08:29:59 +0000244 if (JSONCPP_STRING(argv[index]) == "--json-checker") {
Christopher Dunn79211e12015-01-23 11:27:19 -0600245 opts->features = Json::Features::strictMode();
246 opts->parseOnly = true;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000247 ++index;
248 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000249 if (JSONCPP_STRING(argv[index]) == "--json-config") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000250 printConfig();
251 return 3;
252 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000253 if (JSONCPP_STRING(argv[index]) == "--json-writer") {
Christopher Dunn3682f602015-01-23 11:46:05 -0600254 ++index;
Christopher Dawes75570d72016-03-07 08:29:59 +0000255 JSONCPP_STRING const writerName(argv[index++]);
Christopher Dunn3682f602015-01-23 11:46:05 -0600256 if (writerName == "StyledWriter") {
257 opts->write = &useStyledWriter;
258 } else if (writerName == "StyledStreamWriter") {
259 opts->write = &useStyledStreamWriter;
Christopher Dunn9243d602015-01-23 08:38:32 -0600260 } else if (writerName == "BuiltStyledStreamWriter") {
261 opts->write = &useBuiltStyledStreamWriter;
Christopher Dunn3682f602015-01-23 11:46:05 -0600262 } else {
263 printf("Unknown '--json-writer %s'\n", writerName.c_str());
264 return 4;
265 }
266 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000267 if (index == argc || index + 1 < argc) {
268 return printUsage(argv);
269 }
Christopher Dunn79211e12015-01-23 11:27:19 -0600270 opts->path = argv[index];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000271 return 0;
272}
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600273static int runTest(Options const& opts)
Christopher Dunn79211e12015-01-23 11:27:19 -0600274{
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600275 int exitCode = 0;
276
Christopher Dawes75570d72016-03-07 08:29:59 +0000277 JSONCPP_STRING input = readInputTestFile(opts.path.c_str());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600278 if (input.empty()) {
279 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
280 return 3;
281 }
282
Christopher Dawes75570d72016-03-07 08:29:59 +0000283 JSONCPP_STRING basePath = removeSuffix(opts.path, ".json");
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600284 if (!opts.parseOnly && basePath.empty()) {
285 printf("Bad input path. Path does not end with '.expected':\n%s\n",
286 opts.path.c_str());
287 return 3;
288 }
289
Christopher Dawes75570d72016-03-07 08:29:59 +0000290 JSONCPP_STRING const actualPath = basePath + ".actual";
291 JSONCPP_STRING const rewritePath = basePath + ".rewrite";
292 JSONCPP_STRING const rewriteActualPath = basePath + ".actual-rewrite";
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600293
294 Json::Value root;
295 exitCode = parseAndSaveValueTree(
296 input, actualPath, "input",
297 opts.features, opts.parseOnly, &root);
298 if (exitCode || opts.parseOnly) {
299 return exitCode;
300 }
Christopher Dawes75570d72016-03-07 08:29:59 +0000301 JSONCPP_STRING rewrite;
Christopher Dunn3682f602015-01-23 11:46:05 -0600302 exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600303 if (exitCode) {
304 return exitCode;
305 }
306 Json::Value rewriteRoot;
307 exitCode = parseAndSaveValueTree(
308 rewrite, rewriteActualPath, "rewrite",
309 opts.features, opts.parseOnly, &rewriteRoot);
310 if (exitCode) {
311 return exitCode;
312 }
313 return 0;
Christopher Dunn79211e12015-01-23 11:27:19 -0600314}
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000315int main(int argc, const char* argv[]) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600316 Options opts;
Gaurav6c145482015-09-22 13:53:19 +0530317 try {
Christopher Dunn79211e12015-01-23 11:27:19 -0600318 int exitCode = parseCommandLine(argc, argv, &opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000319 if (exitCode != 0) {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600320 printf("Failed to parse command-line.");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000321 return exitCode;
322 }
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600323 return runTest(opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000324 }
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000325 catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000326 printf("Unhandled exception:\n%s\n", e.what());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600327 return 1;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000328 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000329}
Christopher Dunn90591c72017-08-28 08:38:29 -0500330
331#pragma GCC diagnostic pop