blob: ba985877db4d6a18bebc68d31aef502d8faf184c [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
11#include <stdio.h>
12
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100013#if defined(_MSC_VER) && _MSC_VER >= 1310
14#pragma warning(disable : 4996) // disable fopen deprecation warning
Christopher Dunnf9864232007-06-14 21:01:26 +000015#endif
16
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100017static std::string normalizeFloatingPointStr(double value) {
18 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000019#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100020 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000021#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100022 snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000023#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100024 buffer[sizeof(buffer) - 1] = 0;
25 std::string s(buffer);
26 std::string::size_type index = s.find_last_of("eE");
27 if (index != std::string::npos) {
28 std::string::size_type hasSign =
29 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
30 std::string::size_type exponentStartIndex = index + 1 + hasSign;
31 std::string normalized = s.substr(0, exponentStartIndex);
32 std::string::size_type indexDigit =
33 s.find_first_not_of('0', exponentStartIndex);
34 std::string exponent = "0";
35 if (indexDigit !=
36 std::string::npos) // There is an exponent different from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000037 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100038 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000039 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100040 return normalized + exponent;
41 }
42 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000043}
44
Aaron Jacobs11086dd2014-09-15 10:15:29 +100045static std::string readInputTestFile(const char* path) {
46 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100047 if (!file)
48 return std::string("");
49 fseek(file, 0, SEEK_END);
50 long size = ftell(file);
51 fseek(file, 0, SEEK_SET);
52 std::string text;
Aaron Jacobs11086dd2014-09-15 10:15:29 +100053 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100054 buffer[size] = 0;
55 if (fread(buffer, 1, size, file) == (unsigned long)size)
56 text = buffer;
57 fclose(file);
58 delete[] buffer;
59 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000060}
61
Christopher Dunnf9864232007-06-14 21:01:26 +000062static void
Aaron Jacobs11086dd2014-09-15 10:15:29 +100063printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
Cory Quammen4d234922014-10-09 16:29:47 -040064 if (value.hasComment(Json::commentBefore)) {
65 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
66 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100067 switch (value.type()) {
68 case Json::nullValue:
69 fprintf(fout, "%s=null\n", path.c_str());
70 break;
71 case Json::intValue:
72 fprintf(fout,
73 "%s=%s\n",
74 path.c_str(),
75 Json::valueToString(value.asLargestInt()).c_str());
76 break;
77 case Json::uintValue:
78 fprintf(fout,
79 "%s=%s\n",
80 path.c_str(),
81 Json::valueToString(value.asLargestUInt()).c_str());
82 break;
83 case Json::realValue:
84 fprintf(fout,
85 "%s=%s\n",
86 path.c_str(),
87 normalizeFloatingPointStr(value.asDouble()).c_str());
88 break;
89 case Json::stringValue:
90 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
91 break;
92 case Json::booleanValue:
93 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
94 break;
95 case Json::arrayValue: {
96 fprintf(fout, "%s=[]\n", path.c_str());
97 int size = value.size();
98 for (int index = 0; index < size; ++index) {
99 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +0000100#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000101 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000102#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000103 snprintf(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000104#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000105 printValueTree(fout, value[index], path + buffer);
106 }
107 } break;
108 case Json::objectValue: {
109 fprintf(fout, "%s={}\n", path.c_str());
110 Json::Value::Members members(value.getMemberNames());
111 std::sort(members.begin(), members.end());
112 std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
113 for (Json::Value::Members::iterator it = members.begin();
114 it != members.end();
115 ++it) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000116 const std::string& name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000117 printValueTree(fout, value[name], path + suffix + name);
118 }
119 } break;
120 default:
121 break;
122 }
Cory Quammen4d234922014-10-09 16:29:47 -0400123
124 if (value.hasComment(Json::commentAfter)) {
125 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
126 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000127}
128
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000129static int parseAndSaveValueTree(const std::string& input,
130 const std::string& actual,
131 const std::string& kind,
132 Json::Value& root,
133 const Json::Features& features,
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000134 bool parseOnly) {
135 Json::Reader reader(features);
136 bool parsingSuccessful = reader.parse(input, root);
137 if (!parsingSuccessful) {
138 printf("Failed to parse %s file: \n%s\n",
139 kind.c_str(),
140 reader.getFormattedErrorMessages().c_str());
141 return 1;
142 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000143
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000144 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000145 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000146 if (!factual) {
147 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000148 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000149 }
150 printValueTree(factual, root);
151 fclose(factual);
152 }
153 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000154}
155
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000156static int rewriteValueTree(const std::string& rewritePath,
157 const Json::Value& root,
158 std::string& rewrite) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000159 // Json::FastWriter writer;
160 // writer.enableYAMLCompatibility();
161 Json::StyledWriter writer;
162 rewrite = writer.write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000163 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000164 if (!fout) {
165 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
166 return 2;
167 }
168 fprintf(fout, "%s\n", rewrite.c_str());
169 fclose(fout);
170 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000171}
172
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000173static std::string removeSuffix(const std::string& path,
174 const std::string& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000175 if (extension.length() >= path.length())
176 return std::string("");
177 std::string suffix = path.substr(path.length() - extension.length());
178 if (suffix != extension)
179 return std::string("");
180 return path.substr(0, path.length() - extension.length());
181}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000182
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000183static void printConfig() {
184// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000185#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000186 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000187#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000188 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000189#endif
190}
191
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000192static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000193 printf("Usage: %s [--strict] input-json-file", argv[0]);
194 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000195}
196
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000197int parseCommandLine(int argc,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000198 const char* argv[],
199 Json::Features& features,
200 std::string& path,
201 bool& parseOnly) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000202 parseOnly = false;
203 if (argc < 2) {
204 return printUsage(argv);
205 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000206
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000207 int index = 1;
208 if (std::string(argv[1]) == "--json-checker") {
209 features = Json::Features::strictMode();
210 parseOnly = true;
211 ++index;
212 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000213
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000214 if (std::string(argv[1]) == "--json-config") {
215 printConfig();
216 return 3;
217 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000218
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000219 if (index == argc || index + 1 < argc) {
220 return printUsage(argv);
221 }
222
223 path = argv[index];
224 return 0;
225}
226
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000227int main(int argc, const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000228 std::string path;
229 Json::Features features;
230 bool parseOnly;
231 int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
232 if (exitCode != 0) {
233 return exitCode;
234 }
235
236 try {
237 std::string input = readInputTestFile(path.c_str());
238 if (input.empty()) {
239 printf("Failed to read input or empty input: %s\n", path.c_str());
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000240 return 3;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000241 }
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000242
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000243 std::string basePath = removeSuffix(argv[1], ".json");
244 if (!parseOnly && basePath.empty()) {
245 printf("Bad input path. Path does not end with '.expected':\n%s\n",
246 path.c_str());
247 return 3;
248 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000249
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000250 std::string actualPath = basePath + ".actual";
251 std::string rewritePath = basePath + ".rewrite";
252 std::string rewriteActualPath = basePath + ".actual-rewrite";
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000253
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000254 Json::Value root;
255 exitCode = parseAndSaveValueTree(
256 input, actualPath, "input", root, features, parseOnly);
257 if (exitCode == 0 && !parseOnly) {
258 std::string rewrite;
259 exitCode = rewriteValueTree(rewritePath, root, rewrite);
260 if (exitCode == 0) {
261 Json::Value rewriteRoot;
262 exitCode = parseAndSaveValueTree(rewrite,
263 rewriteActualPath,
264 "rewrite",
265 rewriteRoot,
266 features,
267 parseOnly);
Christopher Dunnf9864232007-06-14 21:01:26 +0000268 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000269 }
270 }
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000271 catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000272 printf("Unhandled exception:\n%s\n", e.what());
273 exitCode = 1;
274 }
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000275
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000276 return exitCode;
Christopher Dunnf9864232007-06-14 21:01:26 +0000277}