1 // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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
6 /* This executable is used for testing parser/writer using real JSON files.
10 #include <algorithm> // sort
14 #if defined(_MSC_VER) && _MSC_VER >= 1310
15 #pragma warning(disable : 4996) // disable fopen deprecation warning
21 Json::Features features;
23 typedef JSONCPP_STRING (*writeFuncType)(Json::Value const&);
27 static JSONCPP_STRING normalizeFloatingPointStr(double value) {
29 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
30 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
32 snprintf(buffer, sizeof(buffer), "%.16g", value);
34 buffer[sizeof(buffer) - 1] = 0;
35 JSONCPP_STRING s(buffer);
36 JSONCPP_STRING::size_type index = s.find_last_of("eE");
37 if (index != JSONCPP_STRING::npos) {
38 JSONCPP_STRING::size_type hasSign =
39 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
40 JSONCPP_STRING::size_type exponentStartIndex = index + 1 + hasSign;
41 JSONCPP_STRING normalized = s.substr(0, exponentStartIndex);
42 JSONCPP_STRING::size_type indexDigit =
43 s.find_first_not_of('0', exponentStartIndex);
44 JSONCPP_STRING exponent = "0";
46 JSONCPP_STRING::npos) // There is an exponent different from 0
48 exponent = s.substr(indexDigit);
50 return normalized + exponent;
55 static JSONCPP_STRING readInputTestFile(const char* path) {
56 FILE* file = fopen(path, "rb");
58 return JSONCPP_STRING("");
59 fseek(file, 0, SEEK_END);
60 long const size = ftell(file);
61 unsigned long const usize = static_cast<unsigned long>(size);
62 fseek(file, 0, SEEK_SET);
64 char* buffer = new char[size + 1];
66 if (fread(buffer, 1, usize, file) == usize)
74 printValueTree(FILE* fout, Json::Value& value, const JSONCPP_STRING& path = ".") {
75 if (value.hasComment(Json::commentBefore)) {
76 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
78 switch (value.type()) {
80 fprintf(fout, "%s=null\n", path.c_str());
86 Json::valueToString(value.asLargestInt()).c_str());
92 Json::valueToString(value.asLargestUInt()).c_str());
98 normalizeFloatingPointStr(value.asDouble()).c_str());
100 case Json::stringValue:
101 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
103 case Json::booleanValue:
104 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
106 case Json::arrayValue: {
107 fprintf(fout, "%s=[]\n", path.c_str());
108 Json::ArrayIndex size = value.size();
109 for (Json::ArrayIndex index = 0; index < size; ++index) {
110 static char buffer[16];
111 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
112 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
114 snprintf(buffer, sizeof(buffer), "[%d]", index);
116 printValueTree(fout, value[index], path + buffer);
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 JSONCPP_STRING suffix = *(path.end() - 1) == '.' ? "" : ".";
124 for (Json::Value::Members::iterator it = members.begin();
127 const JSONCPP_STRING name = *it;
128 printValueTree(fout, value[name], path + suffix + name);
135 if (value.hasComment(Json::commentAfter)) {
136 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
140 static int parseAndSaveValueTree(const JSONCPP_STRING& input,
141 const JSONCPP_STRING& actual,
142 const JSONCPP_STRING& kind,
143 const Json::Features& features,
147 Json::Reader reader(features);
148 bool parsingSuccessful = reader.parse(input.data(), input.data() + input.size(), *root);
149 if (!parsingSuccessful) {
150 printf("Failed to parse %s file: \n%s\n",
152 reader.getFormattedErrorMessages().c_str());
156 FILE* factual = fopen(actual.c_str(), "wt");
158 printf("Failed to create %s actual file.\n", kind.c_str());
161 printValueTree(factual, *root);
166 // static JSONCPP_STRING useFastWriter(Json::Value const& root) {
167 // Json::FastWriter writer;
168 // writer.enableYAMLCompatibility();
169 // return writer.write(root);
171 static JSONCPP_STRING useStyledWriter(
172 Json::Value const& root)
174 Json::StyledWriter writer;
175 return writer.write(root);
177 static JSONCPP_STRING useStyledStreamWriter(
178 Json::Value const& root)
180 Json::StyledStreamWriter writer;
181 JSONCPP_OSTRINGSTREAM sout;
182 writer.write(sout, root);
185 static JSONCPP_STRING useBuiltStyledStreamWriter(
186 Json::Value const& root)
188 Json::StreamWriterBuilder builder;
189 return Json::writeString(builder, root);
191 static int rewriteValueTree(
192 const JSONCPP_STRING& rewritePath,
193 const Json::Value& root,
194 Options::writeFuncType write,
195 JSONCPP_STRING* rewrite)
197 *rewrite = write(root);
198 FILE* fout = fopen(rewritePath.c_str(), "wt");
200 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
203 fprintf(fout, "%s\n", rewrite->c_str());
208 static JSONCPP_STRING removeSuffix(const JSONCPP_STRING& path,
209 const JSONCPP_STRING& extension) {
210 if (extension.length() >= path.length())
211 return JSONCPP_STRING("");
212 JSONCPP_STRING suffix = path.substr(path.length() - extension.length());
213 if (suffix != extension)
214 return JSONCPP_STRING("");
215 return path.substr(0, path.length() - extension.length());
218 static void printConfig() {
219 // Print the configuration used to compile JsonCpp
220 #if defined(JSON_NO_INT64)
221 printf("JSON_NO_INT64=1\n");
223 printf("JSON_NO_INT64=0\n");
227 static int printUsage(const char* argv[]) {
228 printf("Usage: %s [--strict] input-json-file", argv[0]);
232 static int parseCommandLine(
233 int argc, const char* argv[], Options* opts)
235 opts->parseOnly = false;
236 opts->write = &useStyledWriter;
238 return printUsage(argv);
241 if (JSONCPP_STRING(argv[index]) == "--json-checker") {
242 opts->features = Json::Features::strictMode();
243 opts->parseOnly = true;
246 if (JSONCPP_STRING(argv[index]) == "--json-config") {
250 if (JSONCPP_STRING(argv[index]) == "--json-writer") {
252 JSONCPP_STRING const writerName(argv[index++]);
253 if (writerName == "StyledWriter") {
254 opts->write = &useStyledWriter;
255 } else if (writerName == "StyledStreamWriter") {
256 opts->write = &useStyledStreamWriter;
257 } else if (writerName == "BuiltStyledStreamWriter") {
258 opts->write = &useBuiltStyledStreamWriter;
260 printf("Unknown '--json-writer %s'\n", writerName.c_str());
264 if (index == argc || index + 1 < argc) {
265 return printUsage(argv);
267 opts->path = argv[index];
270 static int runTest(Options const& opts)
274 JSONCPP_STRING input = readInputTestFile(opts.path.c_str());
276 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
280 JSONCPP_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",
287 JSONCPP_STRING const actualPath = basePath + ".actual";
288 JSONCPP_STRING const rewritePath = basePath + ".rewrite";
289 JSONCPP_STRING const rewriteActualPath = basePath + ".actual-rewrite";
292 exitCode = parseAndSaveValueTree(
293 input, actualPath, "input",
294 opts.features, opts.parseOnly, &root);
295 if (exitCode || opts.parseOnly) {
298 JSONCPP_STRING rewrite;
299 exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
303 Json::Value rewriteRoot;
304 exitCode = parseAndSaveValueTree(
305 rewrite, rewriteActualPath, "rewrite",
306 opts.features, opts.parseOnly, &rewriteRoot);
312 int main(int argc, const char* argv[]) {
315 int exitCode = parseCommandLine(argc, argv, &opts);
317 printf("Failed to parse command-line.");
320 return runTest(opts);
322 catch (const std::exception& e) {
323 printf("Unhandled exception:\n%s\n", e.what());