blob: 4805af74adfa6d159ab7f7b9847ec0bcc4214fc7 [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
9
Christopher Dunnf9864232007-06-14 21:01:26 +000010#include <json/json.h>
11#include <algorithm> // sort
12#include <stdio.h>
13
14#if defined(_MSC_VER) && _MSC_VER >= 1310
15# pragma warning( disable: 4996 ) // disable fopen deprecation warning
16#endif
17
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000018static std::string
19normalizeFloatingPointStr( double value )
20{
21 char buffer[32];
Aaron Jacobs36400ac2013-08-08 00:39:32 +000022 snprintf( buffer, sizeof(buffer), "%.16g", value );
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000023 buffer[sizeof(buffer)-1] = 0;
24 std::string s( buffer );
25 std::string::size_type index = s.find_last_of( "eE" );
26 if ( index != std::string::npos )
27 {
28 std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0;
29 std::string::size_type exponentStartIndex = index + 1 + hasSign;
30 std::string normalized = s.substr( 0, exponentStartIndex );
31 std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex );
32 std::string exponent = "0";
33 if ( indexDigit != std::string::npos ) // There is an exponent different from 0
34 {
35 exponent = s.substr( indexDigit );
36 }
37 return normalized + exponent;
38 }
39 return s;
40}
41
42
Christopher Dunnf9864232007-06-14 21:01:26 +000043static std::string
44readInputTestFile( const char *path )
45{
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
Christopher Dunnf9864232007-06-14 21:01:26 +000062static void
63printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
64{
65 switch ( value.type() )
66 {
67 case Json::nullValue:
68 fprintf( fout, "%s=null\n", path.c_str() );
69 break;
70 case Json::intValue:
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +000071 fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000072 break;
73 case Json::uintValue:
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +000074 fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000075 break;
76 case Json::realValue:
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000077 fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000078 break;
79 case Json::stringValue:
80 fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
81 break;
82 case Json::booleanValue:
83 fprintf( fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
84 break;
85 case Json::arrayValue:
86 {
87 fprintf( fout, "%s=[]\n", path.c_str() );
88 int size = value.size();
89 for ( int index =0; index < size; ++index )
90 {
91 static char buffer[16];
Aaron Jacobs36400ac2013-08-08 00:39:32 +000092 snprintf( buffer, sizeof(buffer), "[%d]", index );
Christopher Dunnf9864232007-06-14 21:01:26 +000093 printValueTree( fout, value[index], path + buffer );
94 }
95 }
96 break;
97 case Json::objectValue:
98 {
99 fprintf( fout, "%s={}\n", path.c_str() );
100 Json::Value::Members members( value.getMemberNames() );
101 std::sort( members.begin(), members.end() );
102 std::string suffix = *(path.end()-1) == '.' ? "" : ".";
103 for ( Json::Value::Members::iterator it = members.begin();
104 it != members.end();
105 ++it )
106 {
107 const std::string &name = *it;
108 printValueTree( fout, value[name], path + suffix + name );
109 }
110 }
111 break;
112 default:
113 break;
114 }
115}
116
117
118static int
119parseAndSaveValueTree( const std::string &input,
120 const std::string &actual,
121 const std::string &kind,
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000122 Json::Value &root,
123 const Json::Features &features,
124 bool parseOnly )
Christopher Dunnf9864232007-06-14 21:01:26 +0000125{
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000126 Json::Reader reader( features );
Christopher Dunnf9864232007-06-14 21:01:26 +0000127 bool parsingSuccessful = reader.parse( input, root );
128 if ( !parsingSuccessful )
129 {
130 printf( "Failed to parse %s file: \n%s\n",
131 kind.c_str(),
Baptiste Lepilleurb2e8ccc2011-05-01 16:27:55 +0000132 reader.getFormattedErrorMessages().c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +0000133 return 1;
134 }
135
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000136 if ( !parseOnly )
Christopher Dunnf9864232007-06-14 21:01:26 +0000137 {
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000138 FILE *factual = fopen( actual.c_str(), "wt" );
139 if ( !factual )
140 {
141 printf( "Failed to create %s actual file.\n", kind.c_str() );
142 return 2;
143 }
144 printValueTree( factual, root );
145 fclose( factual );
Christopher Dunnf9864232007-06-14 21:01:26 +0000146 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000147 return 0;
148}
149
150
151static int
152rewriteValueTree( const std::string &rewritePath,
153 const Json::Value &root,
154 std::string &rewrite )
155{
156 //Json::FastWriter writer;
157 //writer.enableYAMLCompatibility();
158 Json::StyledWriter writer;
159 rewrite = writer.write( root );
160 FILE *fout = fopen( rewritePath.c_str(), "wt" );
161 if ( !fout )
162 {
163 printf( "Failed to create rewrite file: %s\n", rewritePath.c_str() );
164 return 2;
165 }
166 fprintf( fout, "%s\n", rewrite.c_str() );
167 fclose( fout );
168 return 0;
169}
170
171
172static std::string
173removeSuffix( const std::string &path,
174 const std::string &extension )
175{
176 if ( extension.length() >= path.length() )
177 return std::string("");
178 std::string suffix = path.substr( path.length() - extension.length() );
179 if ( suffix != extension )
180 return std::string("");
181 return path.substr( 0, path.length() - extension.length() );
182}
183
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000184
185static void
186printConfig()
187{
188 // Print the configuration used to compile JsonCpp
189#if defined(JSON_NO_INT64)
190 printf( "JSON_NO_INT64=1\n" );
191#else
192 printf( "JSON_NO_INT64=0\n" );
193#endif
194}
195
196
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000197static int
198printUsage( const char *argv[] )
Christopher Dunnf9864232007-06-14 21:01:26 +0000199{
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000200 printf( "Usage: %s [--strict] input-json-file", argv[0] );
201 return 3;
202}
203
204
205int
206parseCommandLine( int argc, const char *argv[],
207 Json::Features &features, std::string &path,
208 bool &parseOnly )
209{
210 parseOnly = false;
211 if ( argc < 2 )
Christopher Dunnf9864232007-06-14 21:01:26 +0000212 {
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000213 return printUsage( argv );
Christopher Dunnf9864232007-06-14 21:01:26 +0000214 }
215
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000216 int index = 1;
217 if ( std::string(argv[1]) == "--json-checker" )
218 {
219 features = Json::Features::strictMode();
220 parseOnly = true;
221 ++index;
222 }
223
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000224 if ( std::string(argv[1]) == "--json-config" )
225 {
226 printConfig();
227 return 3;
228 }
229
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000230 if ( index == argc || index + 1 < argc )
231 {
232 return printUsage( argv );
233 }
234
235 path = argv[index];
236 return 0;
237}
238
239
240int main( int argc, const char *argv[] )
241{
242 std::string path;
243 Json::Features features;
244 bool parseOnly;
245 int exitCode = parseCommandLine( argc, argv, features, path, parseOnly );
246 if ( exitCode != 0 )
247 {
248 return exitCode;
249 }
250
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000251 try
Christopher Dunnf9864232007-06-14 21:01:26 +0000252 {
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000253 std::string input = readInputTestFile( path.c_str() );
254 if ( input.empty() )
Christopher Dunnf9864232007-06-14 21:01:26 +0000255 {
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000256 printf( "Failed to read input or empty input: %s\n", path.c_str() );
257 return 3;
Christopher Dunnf9864232007-06-14 21:01:26 +0000258 }
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000259
260 std::string basePath = removeSuffix( argv[1], ".json" );
261 if ( !parseOnly && basePath.empty() )
262 {
263 printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
264 return 3;
265 }
266
267 std::string actualPath = basePath + ".actual";
268 std::string rewritePath = basePath + ".rewrite";
269 std::string rewriteActualPath = basePath + ".actual-rewrite";
270
271 Json::Value root;
272 exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly );
273 if ( exitCode == 0 && !parseOnly )
274 {
275 std::string rewrite;
276 exitCode = rewriteValueTree( rewritePath, root, rewrite );
277 if ( exitCode == 0 )
278 {
279 Json::Value rewriteRoot;
280 exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
281 "rewrite", rewriteRoot, features, parseOnly );
282 }
283 }
284 }
285 catch ( const std::exception &e )
286 {
287 printf( "Unhandled exception:\n%s\n", e.what() );
288 exitCode = 1;
Christopher Dunnf9864232007-06-14 21:01:26 +0000289 }
290
291 return exitCode;
292}