blob: 338167e9ccb8071ffa5047bc5a267e4430d5e675 [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 = ".") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100064 switch (value.type()) {
65 case Json::nullValue:
66 fprintf(fout, "%s=null\n", path.c_str());
67 break;
68 case Json::intValue:
69 fprintf(fout,
70 "%s=%s\n",
71 path.c_str(),
72 Json::valueToString(value.asLargestInt()).c_str());
73 break;
74 case Json::uintValue:
75 fprintf(fout,
76 "%s=%s\n",
77 path.c_str(),
78 Json::valueToString(value.asLargestUInt()).c_str());
79 break;
80 case Json::realValue:
81 fprintf(fout,
82 "%s=%s\n",
83 path.c_str(),
84 normalizeFloatingPointStr(value.asDouble()).c_str());
85 break;
86 case Json::stringValue:
87 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
88 break;
89 case Json::booleanValue:
90 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
91 break;
92 case Json::arrayValue: {
93 fprintf(fout, "%s=[]\n", path.c_str());
94 int size = value.size();
95 for (int index = 0; index < size; ++index) {
96 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +000097#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100098 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +000099#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000100 snprintf(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000101#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000102 printValueTree(fout, value[index], path + buffer);
103 }
104 } break;
105 case Json::objectValue: {
106 fprintf(fout, "%s={}\n", path.c_str());
107 Json::Value::Members members(value.getMemberNames());
108 std::sort(members.begin(), members.end());
109 std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
110 for (Json::Value::Members::iterator it = members.begin();
111 it != members.end();
112 ++it) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000113 const std::string& name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000114 printValueTree(fout, value[name], path + suffix + name);
115 }
116 } break;
117 default:
118 break;
119 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000120}
121
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000122static int parseAndSaveValueTree(const std::string& input,
123 const std::string& actual,
124 const std::string& kind,
125 Json::Value& root,
126 const Json::Features& features,
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000127 bool parseOnly) {
128 Json::Reader reader(features);
129 bool parsingSuccessful = reader.parse(input, root);
130 if (!parsingSuccessful) {
131 printf("Failed to parse %s file: \n%s\n",
132 kind.c_str(),
133 reader.getFormattedErrorMessages().c_str());
134 return 1;
135 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000136
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000137 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000138 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000139 if (!factual) {
140 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000141 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000142 }
143 printValueTree(factual, root);
144 fclose(factual);
145 }
146 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000147}
148
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000149static int rewriteValueTree(const std::string& rewritePath,
150 const Json::Value& root,
151 std::string& rewrite) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000152 // Json::FastWriter writer;
153 // writer.enableYAMLCompatibility();
154 Json::StyledWriter writer;
155 rewrite = writer.write(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000156 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000157 if (!fout) {
158 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
159 return 2;
160 }
161 fprintf(fout, "%s\n", rewrite.c_str());
162 fclose(fout);
163 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000164}
165
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000166static std::string removeSuffix(const std::string& path,
167 const std::string& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000168 if (extension.length() >= path.length())
169 return std::string("");
170 std::string suffix = path.substr(path.length() - extension.length());
171 if (suffix != extension)
172 return std::string("");
173 return path.substr(0, path.length() - extension.length());
174}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000175
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000176static void printConfig() {
177// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000178#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000179 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000180#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000181 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000182#endif
183}
184
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000185static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000186 printf("Usage: %s [--strict] input-json-file", argv[0]);
187 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000188}
189
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000190int parseCommandLine(int argc,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000191 const char* argv[],
192 Json::Features& features,
193 std::string& path,
194 bool& parseOnly) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000195 parseOnly = false;
196 if (argc < 2) {
197 return printUsage(argv);
198 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000199
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000200 int index = 1;
201 if (std::string(argv[1]) == "--json-checker") {
202 features = Json::Features::strictMode();
203 parseOnly = true;
204 ++index;
205 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000206
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000207 if (std::string(argv[1]) == "--json-config") {
208 printConfig();
209 return 3;
210 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000211
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000212 if (index == argc || index + 1 < argc) {
213 return printUsage(argv);
214 }
215
216 path = argv[index];
217 return 0;
218}
219
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000220int main(int argc, const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000221 std::string path;
222 Json::Features features;
223 bool parseOnly;
224 int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
225 if (exitCode != 0) {
226 return exitCode;
227 }
228
229 try {
230 std::string input = readInputTestFile(path.c_str());
231 if (input.empty()) {
232 printf("Failed to read input or empty input: %s\n", path.c_str());
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000233 return 3;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000234 }
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000235
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000236 std::string basePath = removeSuffix(argv[1], ".json");
237 if (!parseOnly && basePath.empty()) {
238 printf("Bad input path. Path does not end with '.expected':\n%s\n",
239 path.c_str());
240 return 3;
241 }
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000242
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000243 std::string actualPath = basePath + ".actual";
244 std::string rewritePath = basePath + ".rewrite";
245 std::string rewriteActualPath = basePath + ".actual-rewrite";
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000246
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000247 Json::Value root;
248 exitCode = parseAndSaveValueTree(
249 input, actualPath, "input", root, features, parseOnly);
250 if (exitCode == 0 && !parseOnly) {
251 std::string rewrite;
252 exitCode = rewriteValueTree(rewritePath, root, rewrite);
253 if (exitCode == 0) {
254 Json::Value rewriteRoot;
255 exitCode = parseAndSaveValueTree(rewrite,
256 rewriteActualPath,
257 "rewrite",
258 rewriteRoot,
259 features,
260 parseOnly);
Christopher Dunnf9864232007-06-14 21:01:26 +0000261 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000262 }
263 }
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000264 catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000265 printf("Unhandled exception:\n%s\n", e.what());
266 exitCode = 1;
267 }
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000268
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000269 return exitCode;
Christopher Dunnf9864232007-06-14 21:01:26 +0000270}