1 // 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 6 /* This executable is used for testing parser/writer using real JSON files. 7 */ 8 9 #include <json/json.h> 10 #include <algorithm> // sort 11 #include <stdio.h> 12 13 #if defined(_MSC_VER) && _MSC_VER >= 1310 14 #pragma warning(disable : 4996) // disable fopen deprecation warning 15 #endif 16 17 static std::string normalizeFloatingPointStr(double value) { 18 char buffer[32]; 19 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) 20 sprintf_s(buffer, sizeof(buffer), "%.16g", value); 21 #else 22 snprintf(buffer, sizeof(buffer), "%.16g", value); 23 #endif 24 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 37 { 38 exponent = s.substr(indexDigit); 39 } 40 return normalized + exponent; 41 } 42 return s; 43 } 44 45 static std::string readInputTestFile(const char* path) { 46 FILE* file = fopen(path, "rb"); 47 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; 53 char* buffer = new char[size + 1]; 54 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; 60 } 61 62 static void 63 printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") { 64 if (value.hasComment(Json::commentBefore)) { 65 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str()); 66 } 67 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]; 100 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) 101 sprintf_s(buffer, sizeof(buffer), "[%d]", index); 102 #else 103 snprintf(buffer, sizeof(buffer), "[%d]", index); 104 #endif 105 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) { 116 const std::string& name = *it; 117 printValueTree(fout, value[name], path + suffix + name); 118 } 119 } break; 120 default: 121 break; 122 } 123 124 if (value.hasComment(Json::commentAfter)) { 125 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str()); 126 } 127 } 128 129 static 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, 134 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 } 143 144 if (!parseOnly) { 145 FILE* factual = fopen(actual.c_str(), "wt"); 146 if (!factual) { 147 printf("Failed to create %s actual file.\n", kind.c_str()); 148 return 2; 149 } 150 printValueTree(factual, root); 151 fclose(factual); 152 } 153 return 0; 154 } 155 156 static int rewriteValueTree(const std::string& rewritePath, 157 const Json::Value& root, 158 std::string& rewrite) { 159 // Json::FastWriter writer; 160 // writer.enableYAMLCompatibility(); 161 Json::StyledWriter writer; 162 rewrite = writer.write(root); 163 FILE* fout = fopen(rewritePath.c_str(), "wt"); 164 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; 171 } 172 173 static std::string removeSuffix(const std::string& path, 174 const std::string& extension) { 175 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 } 182 183 static void printConfig() { 184 // Print the configuration used to compile JsonCpp 185 #if defined(JSON_NO_INT64) 186 printf("JSON_NO_INT64=1\n"); 187 #else 188 printf("JSON_NO_INT64=0\n"); 189 #endif 190 } 191 192 static int printUsage(const char* argv[]) { 193 printf("Usage: %s [--strict] input-json-file", argv[0]); 194 return 3; 195 } 196 197 int parseCommandLine(int argc, 198 const char* argv[], 199 Json::Features& features, 200 std::string& path, 201 bool& parseOnly) { 202 parseOnly = false; 203 if (argc < 2) { 204 return printUsage(argv); 205 } 206 207 int index = 1; 208 if (std::string(argv[1]) == "--json-checker") { 209 features = Json::Features::strictMode(); 210 parseOnly = true; 211 ++index; 212 } 213 214 if (std::string(argv[1]) == "--json-config") { 215 printConfig(); 216 return 3; 217 } 218 219 if (index == argc || index + 1 < argc) { 220 return printUsage(argv); 221 } 222 223 path = argv[index]; 224 return 0; 225 } 226 227 int main(int argc, const char* argv[]) { 228 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()); 240 return 3; 241 } 242 243 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 } 249 250 std::string actualPath = basePath + ".actual"; 251 std::string rewritePath = basePath + ".rewrite"; 252 std::string rewriteActualPath = basePath + ".actual-rewrite"; 253 254 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); 268 } 269 } 270 } 271 catch (const std::exception& e) { 272 printf("Unhandled exception:\n%s\n", e.what()); 273 exitCode = 1; 274 } 275 276 return exitCode; 277 } 278