// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
/* This executable is used for testing parser/writer using real JSON files.
*/
#include <json/json.h>
#include <algorithm> // sort
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER >= 1310
# pragma warning( disable: 4996 ) // disable fopen deprecation warning
#endif
static std::string
normalizeFloatingPointStr( double value )
{
char buffer[32];
sprintf( buffer, "%.16g", value );
buffer[sizeof(buffer)-1] = 0;
std::string s( buffer );
std::string::size_type index = s.find_last_of( "eE" );
if ( index != std::string::npos )
{
std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0;
std::string::size_type exponentStartIndex = index + 1 + hasSign;
std::string normalized = s.substr( 0, exponentStartIndex );
std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex );
std::string exponent = "0";
if ( indexDigit != std::string::npos ) // There is an exponent different from 0
{
exponent = s.substr( indexDigit );
}
return normalized + exponent;
}
return s;
}
static std::string
readInputTestFile( const char *path )
{
FILE *file = fopen( path, "rb" );
if ( !file )
return std::string("");
fseek( file, 0, SEEK_END );
long size = ftell( file );
fseek( file, 0, SEEK_SET );
std::string text;
char *buffer = new char[size+1];
buffer[size] = 0;
if ( fread( buffer, 1, size, file ) == (unsigned long)size )
text = buffer;
fclose( file );
delete[] buffer;
return text;
}
static void
printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
{
switch ( value.type() )
{
case Json::nullValue:
fprintf( fout, "%s=null\n", path.c_str() );
break;
case Json::intValue:
fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() );
break;
case Json::uintValue:
fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() );
break;
case Json::realValue:
fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() );
break;
case Json::stringValue:
fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
break;
case Json::booleanValue:
fprintf( fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
break;
case Json::arrayValue:
{
fprintf( fout, "%s=[]\n", path.c_str() );
int size = value.size();
for ( int index =0; index < size; ++index )
{
static char buffer[16];
sprintf( buffer, "[%d]", index );
printValueTree( fout, value[index], path + buffer );
}
}
break;
case Json::objectValue:
{
fprintf( fout, "%s={}\n", path.c_str() );
Json::Value::Members members( value.getMemberNames() );
std::sort( members.begin(), members.end() );
std::string suffix = *(path.end()-1) == '.' ? "" : ".";
for ( Json::Value::Members::iterator it = members.begin();
it != members.end();
++it )
{
const std::string &name = *it;
printValueTree( fout, value[name], path + suffix + name );
}
}
break;
default:
break;
}
}
static int
parseAndSaveValueTree( const std::string &input,
const std::string &actual,
const std::string &kind,
Json::Value &root,
const Json::Features &features,
bool parseOnly )
{
Json::Reader reader( features );
bool parsingSuccessful = reader.parse( input, root );
if ( !parsingSuccessful )
{
printf( "Failed to parse %s file: \n%s\n",
kind.c_str(),
reader.getFormattedErrorMessages().c_str() );
return 1;
}
if ( !parseOnly )
{
FILE *factual = fopen( actual.c_str(), "wt" );
if ( !factual )
{
printf( "Failed to create %s actual file.\n", kind.c_str() );
return 2;
}
printValueTree( factual, root );
fclose( factual );
}
return 0;
}
static int
rewriteValueTree( const std::string &rewritePath,
const Json::Value &root,
std::string &rewrite )
{
//Json::FastWriter writer;
//writer.enableYAMLCompatibility();
Json::StyledWriter writer;
rewrite = writer.write( root );
FILE *fout = fopen( rewritePath.c_str(), "wt" );
if ( !fout )
{
printf( "Failed to create rewrite file: %s\n", rewritePath.c_str() );
return 2;
}
fprintf( fout, "%s\n", rewrite.c_str() );
fclose( fout );
return 0;
}
static std::string
removeSuffix( const std::string &path,
const std::string &extension )
{
if ( extension.length() >= path.length() )
return std::string("");
std::string suffix = path.substr( path.length() - extension.length() );
if ( suffix != extension )
return std::string("");
return path.substr( 0, path.length() - extension.length() );
}
static void
printConfig()
{
// Print the configuration used to compile JsonCpp
#if defined(JSON_NO_INT64)
printf( "JSON_NO_INT64=1\n" );
#else
printf( "JSON_NO_INT64=0\n" );
#endif
}
static int
printUsage( const char *argv[] )
{
printf( "Usage: %s [--strict] input-json-file", argv[0] );
return 3;
}
int
parseCommandLine( int argc, const char *argv[],
Json::Features &features, std::string &path,
bool &parseOnly )
{
parseOnly = false;
if ( argc < 2 )
{
return printUsage( argv );
}
int index = 1;
if ( std::string(argv[1]) == "--json-checker" )
{
features = Json::Features::strictMode();
parseOnly = true;
++index;
}
if ( std::string(argv[1]) == "--json-config" )
{
printConfig();
return 3;
}
if ( index == argc || index + 1 < argc )
{
return printUsage( argv );
}
path = argv[index];
return 0;
}
int main( int argc, const char *argv[] )
{
std::string path;
Json::Features features;
bool parseOnly;
int exitCode = parseCommandLine( argc, argv, features, path, parseOnly );
if ( exitCode != 0 )
{
return exitCode;
}
try
{
std::string input = readInputTestFile( path.c_str() );
if ( input.empty() )
{
printf( "Failed to read input or empty input: %s\n", path.c_str() );
return 3;
}
std::string basePath = removeSuffix( argv[1], ".json" );
if ( !parseOnly && basePath.empty() )
{
printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
return 3;
}
std::string actualPath = basePath + ".actual";
std::string rewritePath = basePath + ".rewrite";
std::string rewriteActualPath = basePath + ".actual-rewrite";
Json::Value root;
exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly );
if ( exitCode == 0 && !parseOnly )
{
std::string rewrite;
exitCode = rewriteValueTree( rewritePath, root, rewrite );
if ( exitCode == 0 )
{
Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
"rewrite", rewriteRoot, features, parseOnly );
}
}
}
catch ( const std::exception &e )
{
printf( "Unhandled exception:\n%s\n", e.what() );
exitCode = 1;
}
return exitCode;
}