- added Features class that describes allowed extension for Reader, to allow for strict configuration
- added tests from json.org jsonchecker and modified jsontestrunner to use strict parsing mode when executing them
diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp
index 88ed2f1..231ee0c 100644
--- a/src/jsontestrunner/main.cpp
+++ b/src/jsontestrunner/main.cpp
@@ -86,9 +86,11 @@
parseAndSaveValueTree( const std::string &input,
const std::string &actual,
const std::string &kind,
- Json::Value &root )
+ Json::Value &root,
+ const Json::Features &features,
+ bool parseOnly )
{
- Json::Reader reader;
+ Json::Reader reader( features );
bool parsingSuccessful = reader.parse( input, root );
if ( !parsingSuccessful )
{
@@ -98,14 +100,17 @@
return 1;
}
- FILE *factual = fopen( actual.c_str(), "wt" );
- if ( !factual )
+ if ( !parseOnly )
{
- printf( "Failed to create %s actual file.\n", kind.c_str() );
- return 2;
+ 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 );
}
- printValueTree( factual, root );
- fclose( factual );
return 0;
}
@@ -143,25 +148,65 @@
return path.substr( 0, path.length() - extension.length() );
}
-int main( int argc, const char *argv[] )
+static int
+printUsage( const char *argv[] )
{
- if ( argc != 2 )
+ 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 )
{
- printf( "Usage: %s input-json-file", argv[0] );
- return 3;
+ return printUsage( argv );
}
- std::string input = readInputTestFile( argv[1] );
+ int index = 1;
+ if ( std::string(argv[1]) == "--json-checker" )
+ {
+ features = Json::Features::strictMode();
+ parseOnly = true;
+ ++index;
+ }
+
+ 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;
+ }
+
+ std::string input = readInputTestFile( path.c_str() );
if ( input.empty() )
{
- printf( "Failed to read input or empty input: %s\n", argv[1] );
+ printf( "Failed to read input or empty input: %s\n", path.c_str() );
return 3;
}
std::string basePath = removeSuffix( argv[1], ".json" );
- if ( basePath.empty() )
+ if ( !parseOnly && basePath.empty() )
{
- printf( "Bad input path. Path does not end with '.expected':\n%s\n", argv[1] );
+ printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
return 3;
}
@@ -170,15 +215,16 @@
std::string rewriteActualPath = basePath + ".actual-rewrite";
Json::Value root;
- int exitCode = parseAndSaveValueTree( input, actualPath, "input", root );
- if ( exitCode == 0 )
+ 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 );
+ exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
+ "rewrite", rewriteRoot, features, parseOnly );
}
}
diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp
index 0e0c2ff..7addb3b 100644
--- a/src/lib_json/json_reader.cpp
+++ b/src/lib_json/json_reader.cpp
@@ -13,6 +13,36 @@
namespace Json {
+// Implementation of class Features
+// ////////////////////////////////
+
+Features::Features()
+ : allowComments_( true )
+ , strictRoot_( false )
+{
+}
+
+
+Features
+Features::all()
+{
+ return Features();
+}
+
+
+Features
+Features::strictMode()
+{
+ Features features;
+ features.allowComments_ = false;
+ features.strictRoot_ = true;
+ return features;
+}
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+
static inline bool
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
{
@@ -77,9 +107,17 @@
// //////////////////////////////////////////////////////////////////
Reader::Reader()
+ : features_( Features::all() )
{
}
+
+Reader::Reader( const Features &features )
+ : features_( features )
+{
+}
+
+
bool
Reader::parse( const std::string &document,
Value &root,
@@ -91,6 +129,7 @@
return parse( begin, end, root, collectComments );
}
+
bool
Reader::parse( std::istream& sin,
Value &root,
@@ -113,6 +152,11 @@
Value &root,
bool collectComments )
{
+ if ( !features_.allowComments_ )
+ {
+ collectComments = false;
+ }
+
begin_ = beginDoc;
end_ = endDoc;
collectComments_ = collectComments;
@@ -130,6 +174,19 @@
skipCommentTokens( token );
if ( collectComments_ && !commentsBefore_.empty() )
root.setComment( commentsBefore_, commentAfter );
+ if ( features_.strictRoot_ )
+ {
+ if ( !root.isArray() && !root.isObject() )
+ {
+ // Set error location to start of doc, ideally should be first token found in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError( "A valid JSON document must be either an array or an object value.",
+ token );
+ return false;
+ }
+ }
return successful;
}
@@ -188,11 +245,18 @@
void
Reader::skipCommentTokens( Token &token )
{
- do
+ if ( features_.allowComments_ )
+ {
+ do
+ {
+ readToken( token );
+ }
+ while ( token.type_ == tokenComment );
+ }
+ else
{
readToken( token );
}
- while ( token.type_ == tokenComment );
}
diff --git a/src/lib_json/lib_json.vcproj b/src/lib_json/lib_json.vcproj
index 3fa9cf0..aa47ffa 100644
--- a/src/lib_json/lib_json.vcproj
+++ b/src/lib_json/lib_json.vcproj
@@ -170,6 +170,9 @@
RelativePath="..\..\include\json\config.h">
</File>
<File
+ RelativePath="..\..\include\json\features.h">
+ </File>
+ <File
RelativePath="..\..\include\json\forwards.h">
</File>
<File