blob: a0c88bd79acb0c97c1708954f5b2cd688fe456e7 [file] [log] [blame]
Baptiste Lepilleur7469f1d2010-04-20 21:35:19 +00001// Copyright 2007-2010 Baptiste Lepilleur
2// 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
Baptiste Lepilleurfa130ef2010-12-24 12:47:14 +00006/* This executable is used for testing parser/writer using real JSON files.
7 */
8
Christopher Dunnf9864232007-06-14 21:01:26 +00009#include <json/json.h>
10#include <algorithm> // sort
Christopher Dunn2160c9a2015-01-23 09:02:44 -060011#include <sstream>
Christopher Dunnf9864232007-06-14 21:01:26 +000012#include <stdio.h>
13
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100014#if defined(_MSC_VER) && _MSC_VER >= 1310
15#pragma warning(disable : 4996) // disable fopen deprecation warning
Christopher Dunnf9864232007-06-14 21:01:26 +000016#endif
17
Christopher Dunn3682f602015-01-23 11:46:05 -060018struct Options
19{
20 std::string path;
21 Json::Features features;
22 bool parseOnly;
23 typedef std::string (*writeFuncType)(Json::Value const&);
24 writeFuncType write;
25};
26
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100027static std::string normalizeFloatingPointStr(double value) {
28 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000029#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100030 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000031#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100032 snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000033#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100034 buffer[sizeof(buffer) - 1] = 0;
35 std::string s(buffer);
36 std::string::size_type index = s.find_last_of("eE");
37 if (index != std::string::npos) {
38 std::string::size_type hasSign =
39 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
40 std::string::size_type exponentStartIndex = index + 1 + hasSign;
41 std::string normalized = s.substr(0, exponentStartIndex);
42 std::string::size_type indexDigit =
43 s.find_first_not_of('0', exponentStartIndex);
44 std::string exponent = "0";
45 if (indexDigit !=
46 std::string::npos) // There is an exponent different from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000047 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100048 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000049 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100050 return normalized + exponent;
51 }
52 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000053}
54
Aaron Jacobs11086dd2014-09-15 10:15:29 +100055static std::string readInputTestFile(const char* path) {
56 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100057 if (!file)
58 return std::string("");
59 fseek(file, 0, SEEK_END);
Christopher Dunnd4513fc2016-02-06 09:25:20 -060060 long const size = ftell(file);
61 unsigned long const usize = static_cast<unsigned long const>(size);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100062 fseek(file, 0, SEEK_SET);
63 std::string text;
Aaron Jacobs11086dd2014-09-15 10:15:29 +100064 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100065 buffer[size] = 0;
Christopher Dunnd4513fc2016-02-06 09:25:20 -060066 if (fread(buffer, 1, usize, file) == usize)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100067 text = buffer;
68 fclose(file);
69 delete[] buffer;
70 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000071}
72
Christopher Dunnf9864232007-06-14 21:01:26 +000073static void
Aaron Jacobs11086dd2014-09-15 10:15:29 +100074printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
Cory Quammen4d234922014-10-09 16:29:47 -040075 if (value.hasComment(Json::commentBefore)) {
76 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
77 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100078 switch (value.type()) {
79 case Json::nullValue:
80 fprintf(fout, "%s=null\n", path.c_str());
81 break;
82 case Json::intValue:
83 fprintf(fout,
84 "%s=%s\n",
85 path.c_str(),
86 Json::valueToString(value.asLargestInt()).c_str());
87 break;
88 case Json::uintValue:
89 fprintf(fout,
90 "%s=%s\n",
91 path.c_str(),
92 Json::valueToString(value.asLargestUInt()).c_str());
93 break;
94 case Json::realValue:
95 fprintf(fout,
96 "%s=%s\n",
97 path.c_str(),
98 normalizeFloatingPointStr(value.asDouble()).c_str());
99 break;
100 case Json::stringValue:
101 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
102 break;
103 case Json::booleanValue:
104 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
105 break;
106 case Json::arrayValue: {
107 fprintf(fout, "%s=[]\n", path.c_str());
Christopher Dunnd4513fc2016-02-06 09:25:20 -0600108 Json::ArrayIndex size = value.size();
109 for (Json::ArrayIndex index = 0; index < size; ++index) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000110 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +0000111#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000112 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000113#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000114 snprintf(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000115#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000116 printValueTree(fout, value[index], path + buffer);
117 }
118 } break;
119 case Json::objectValue: {
120 fprintf(fout, "%s={}\n", path.c_str());
121 Json::Value::Members members(value.getMemberNames());
122 std::sort(members.begin(), members.end());
123 std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
124 for (Json::Value::Members::iterator it = members.begin();
125 it != members.end();
126 ++it) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000127 const std::string& name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000128 printValueTree(fout, value[name], path + suffix + name);
129 }
130 } break;
131 default:
132 break;
133 }
Cory Quammen4d234922014-10-09 16:29:47 -0400134
135 if (value.hasComment(Json::commentAfter)) {
136 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
137 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000138}
139
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000140static int parseAndSaveValueTree(const std::string& input,
141 const std::string& actual,
142 const std::string& kind,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000143 const Json::Features& features,
Christopher Dunn632c9b52015-01-23 11:09:04 -0600144 bool parseOnly,
Christopher Dunn79211e12015-01-23 11:27:19 -0600145 Json::Value* root)
146{
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000147 Json::Reader reader(features);
Christopher Dunn632c9b52015-01-23 11:09:04 -0600148 bool parsingSuccessful = reader.parse(input, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000149 if (!parsingSuccessful) {
150 printf("Failed to parse %s file: \n%s\n",
151 kind.c_str(),
152 reader.getFormattedErrorMessages().c_str());
153 return 1;
154 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000155 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000156 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000157 if (!factual) {
158 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000159 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000160 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600161 printValueTree(factual, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000162 fclose(factual);
163 }
164 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000165}
Christopher Dunn79211e12015-01-23 11:27:19 -0600166// static std::string useFastWriter(Json::Value const& root) {
167// Json::FastWriter writer;
168// writer.enableYAMLCompatibility();
169// return writer.write(root);
170// }
171static std::string useStyledWriter(
172 Json::Value const& root)
173{
174 Json::StyledWriter writer;
175 return writer.write(root);
176}
177static std::string useStyledStreamWriter(
178 Json::Value const& root)
179{
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600180 Json::StyledStreamWriter writer;
Christopher Dunn38bb4912016-03-06 11:50:00 -0600181 JSONCPP_OSTRINGSTREAM sout;
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600182 writer.write(sout, root);
Christopher Dunn79211e12015-01-23 11:27:19 -0600183 return sout.str();
184}
Christopher Dunn9243d602015-01-23 08:38:32 -0600185static std::string useBuiltStyledStreamWriter(
186 Json::Value const& root)
187{
Christopher Dunn6065a1c2015-01-26 11:01:15 -0600188 Json::StreamWriterBuilder builder;
Christopher Dunn694dbcb2015-02-09 15:25:57 -0600189 return Json::writeString(builder, root);
Christopher Dunn9243d602015-01-23 08:38:32 -0600190}
Christopher Dunn79211e12015-01-23 11:27:19 -0600191static int rewriteValueTree(
192 const std::string& rewritePath,
193 const Json::Value& root,
Christopher Dunn3682f602015-01-23 11:46:05 -0600194 Options::writeFuncType write,
Christopher Dunn79211e12015-01-23 11:27:19 -0600195 std::string* rewrite)
196{
Christopher Dunn3682f602015-01-23 11:46:05 -0600197 *rewrite = write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000198 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000199 if (!fout) {
200 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
201 return 2;
202 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600203 fprintf(fout, "%s\n", rewrite->c_str());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000204 fclose(fout);
205 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000206}
207
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000208static std::string removeSuffix(const std::string& path,
209 const std::string& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000210 if (extension.length() >= path.length())
211 return std::string("");
212 std::string suffix = path.substr(path.length() - extension.length());
213 if (suffix != extension)
214 return std::string("");
215 return path.substr(0, path.length() - extension.length());
216}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000217
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000218static void printConfig() {
219// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000220#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000221 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000222#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000223 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000224#endif
225}
226
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000227static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000228 printf("Usage: %s [--strict] input-json-file", argv[0]);
229 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000230}
231
Christopher Dunn79211e12015-01-23 11:27:19 -0600232static int parseCommandLine(
233 int argc, const char* argv[], Options* opts)
234{
235 opts->parseOnly = false;
Christopher Dunn3682f602015-01-23 11:46:05 -0600236 opts->write = &useStyledWriter;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000237 if (argc < 2) {
238 return printUsage(argv);
239 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000240 int index = 1;
Christopher Dunn3682f602015-01-23 11:46:05 -0600241 if (std::string(argv[index]) == "--json-checker") {
Christopher Dunn79211e12015-01-23 11:27:19 -0600242 opts->features = Json::Features::strictMode();
243 opts->parseOnly = true;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000244 ++index;
245 }
Christopher Dunn08cfd022015-01-23 11:33:47 -0600246 if (std::string(argv[index]) == "--json-config") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000247 printConfig();
248 return 3;
249 }
Christopher Dunn3682f602015-01-23 11:46:05 -0600250 if (std::string(argv[index]) == "--json-writer") {
251 ++index;
252 std::string const writerName(argv[index++]);
253 if (writerName == "StyledWriter") {
254 opts->write = &useStyledWriter;
255 } else if (writerName == "StyledStreamWriter") {
256 opts->write = &useStyledStreamWriter;
Christopher Dunn9243d602015-01-23 08:38:32 -0600257 } else if (writerName == "BuiltStyledStreamWriter") {
258 opts->write = &useBuiltStyledStreamWriter;
Christopher Dunn3682f602015-01-23 11:46:05 -0600259 } else {
260 printf("Unknown '--json-writer %s'\n", writerName.c_str());
261 return 4;
262 }
263 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000264 if (index == argc || index + 1 < argc) {
265 return printUsage(argv);
266 }
Christopher Dunn79211e12015-01-23 11:27:19 -0600267 opts->path = argv[index];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000268 return 0;
269}
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600270static int runTest(Options const& opts)
Christopher Dunn79211e12015-01-23 11:27:19 -0600271{
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600272 int exitCode = 0;
273
274 std::string input = readInputTestFile(opts.path.c_str());
275 if (input.empty()) {
276 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
277 return 3;
278 }
279
280 std::string basePath = removeSuffix(opts.path, ".json");
281 if (!opts.parseOnly && basePath.empty()) {
282 printf("Bad input path. Path does not end with '.expected':\n%s\n",
283 opts.path.c_str());
284 return 3;
285 }
286
287 std::string const actualPath = basePath + ".actual";
288 std::string const rewritePath = basePath + ".rewrite";
289 std::string const rewriteActualPath = basePath + ".actual-rewrite";
290
291 Json::Value root;
292 exitCode = parseAndSaveValueTree(
293 input, actualPath, "input",
294 opts.features, opts.parseOnly, &root);
295 if (exitCode || opts.parseOnly) {
296 return exitCode;
297 }
298 std::string rewrite;
Christopher Dunn3682f602015-01-23 11:46:05 -0600299 exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600300 if (exitCode) {
301 return exitCode;
302 }
303 Json::Value rewriteRoot;
304 exitCode = parseAndSaveValueTree(
305 rewrite, rewriteActualPath, "rewrite",
306 opts.features, opts.parseOnly, &rewriteRoot);
307 if (exitCode) {
308 return exitCode;
309 }
310 return 0;
Christopher Dunn79211e12015-01-23 11:27:19 -0600311}
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000312int main(int argc, const char* argv[]) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600313 Options opts;
Gaurav6c145482015-09-22 13:53:19 +0530314 try {
Christopher Dunn79211e12015-01-23 11:27:19 -0600315 int exitCode = parseCommandLine(argc, argv, &opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000316 if (exitCode != 0) {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600317 printf("Failed to parse command-line.");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000318 return exitCode;
319 }
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600320 return runTest(opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000321 }
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000322 catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000323 printf("Unhandled exception:\n%s\n", e.what());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600324 return 1;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000325 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000326}