Added structured error reporting to Reader.

This allows applications for interactively viewing or editing JSON to do
a better job of highlighting errors. Also added offset accessors to
Value, offering the same sort of functionality even for non-errors.

Thanks to Zach Clifford (zacharyc@google.com) for the patch.
This commit is contained in:
Aaron Jacobs
2014-04-23 23:41:12 +00:00
parent 642befc836
commit 68db655347
5 changed files with 269 additions and 3 deletions

View File

@@ -215,9 +215,11 @@ Reader::readValue()
{
case tokenObjectBegin:
successful = readObject( token );
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenArrayBegin:
successful = readArray( token );
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenNumber:
successful = decodeNumber( token );
@@ -227,12 +229,18 @@ Reader::readValue()
break;
case tokenTrue:
currentValue() = true;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenFalse:
currentValue() = false;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenNull:
currentValue() = Value();
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
break;
case tokenArraySeparator:
if ( features_.allowDroppedNullPlaceholders_ )
@@ -241,10 +249,14 @@ Reader::readValue()
// token.
current_--;
currentValue() = Value();
currentValue().setOffsetStart(current_ - begin_ - 1);
currentValue().setOffsetLimit(current_ - begin_);
break;
}
// Else, fall through...
default:
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return addError( "Syntax error: value, object or array expected.", token );
}
@@ -493,11 +505,12 @@ Reader::readString()
bool
Reader::readObject( Token &/*tokenStart*/ )
Reader::readObject( Token &tokenStart )
{
Token tokenName;
std::string name;
currentValue() = Value( objectValue );
currentValue().setOffsetStart(tokenStart.start_ - begin_);
while ( readToken( tokenName ) )
{
bool initialTokenOk = true;
@@ -564,9 +577,10 @@ Reader::readObject( Token &/*tokenStart*/ )
bool
Reader::readArray( Token &/*tokenStart*/ )
Reader::readArray( Token &tokenStart )
{
currentValue() = Value( arrayValue );
currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces();
if ( *current_ == ']' ) // empty array
{
@@ -613,6 +627,8 @@ Reader::decodeNumber( Token &token )
if ( !decodeNumber( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -678,6 +694,8 @@ Reader::decodeDouble( Token &token )
if ( !decodeDouble( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -729,6 +747,8 @@ Reader::decodeString( Token &token )
if ( !decodeString( token, decoded ) )
return false;
currentValue() = decoded;
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -963,6 +983,25 @@ Reader::getFormattedErrorMessages() const
}
std::vector<Reader::StructuredError>
Reader::getStructuredErrors() const
{
std::vector<Reader::StructuredError> allErrors;
for ( Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError )
{
const ErrorInfo &error = *itError;
Reader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_;
allErrors.push_back(structured);
}
return allErrors;
}
std::istream& operator>>( std::istream &sin, Value &root )
{
Json::Reader reader;

View File

@@ -274,6 +274,8 @@ Value::Value( ValueType type )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
switch ( type )
{
@@ -318,6 +320,8 @@ Value::Value( UInt value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.uint_ = value;
}
@@ -329,6 +333,8 @@ Value::Value( Int value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.int_ = value;
}
@@ -342,6 +348,8 @@ Value::Value( Int64 value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.int_ = value;
}
@@ -354,6 +362,8 @@ Value::Value( UInt64 value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.uint_ = value;
}
@@ -366,6 +376,8 @@ Value::Value( double value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.real_ = value;
}
@@ -377,6 +389,8 @@ Value::Value( const char *value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value );
}
@@ -390,6 +404,8 @@ Value::Value( const char *beginValue,
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( beginValue,
(unsigned int)(endValue - beginValue) );
@@ -403,6 +419,8 @@ Value::Value( const std::string &value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value.c_str(),
(unsigned int)value.length() );
@@ -416,6 +434,8 @@ Value::Value( const StaticString &value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = const_cast<char *>( value.c_str() );
}
@@ -429,6 +449,8 @@ Value::Value( const CppTL::ConstString &value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.string_ = duplicateStringValue( value, value.length() );
}
@@ -441,6 +463,8 @@ Value::Value( bool value )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( 0 )
, limit_( 0 )
{
value_.bool_ = value;
}
@@ -453,6 +477,8 @@ Value::Value( const Value &other )
, itemIsUsed_( 0 )
#endif
, comments_( 0 )
, start_( other.start_ )
, limit_( other.limit_ )
{
switch ( type_ )
{
@@ -557,6 +583,8 @@ Value::swap( Value &other )
int temp2 = allocated_;
allocated_ = other.allocated_;
other.allocated_ = temp2;
std::swap( start_, other.start_ );
std::swap( limit_, other.limit_ );
}
ValueType
@@ -1027,7 +1055,8 @@ void
Value::clear()
{
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue || type_ == objectValue, "in Json::Value::clear(): requires complex value" );
start_ = 0;
limit_ = 0;
switch ( type_ )
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1556,6 +1585,34 @@ Value::getComment( CommentPlacement placement ) const
}
void
Value::setOffsetStart( size_t start )
{
start_ = start;
}
void
Value::setOffsetLimit( size_t limit )
{
limit_ = limit;
}
size_t
Value::getOffsetStart() const
{
return start_;
}
size_t
Value::getOffsetLimit() const
{
return limit_;
}
std::string
Value::toStyledString() const
{