Skip to content

Commit 68db655

Browse files
committed
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 ([email protected]) for the patch.
1 parent 642befc commit 68db655

File tree

5 files changed

+269
-3
lines changed

5 files changed

+269
-3
lines changed

include/json/reader.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ namespace Json {
3333
typedef char Char;
3434
typedef const Char *Location;
3535

36+
/** \brief An error tagged with where in the JSON text it was encountered.
37+
*
38+
* The offsets give the [start, limit) range of bytes within the text. Note
39+
* that this is bytes, not codepoints.
40+
*
41+
*/
42+
struct StructuredError
43+
{
44+
size_t offset_start;
45+
size_t offset_limit;
46+
std::string message;
47+
};
48+
3649
/** \brief Constructs a Reader allowing all features
3750
* for parsing.
3851
*/
@@ -95,6 +108,14 @@ namespace Json {
95108
*/
96109
std::string getFormattedErrorMessages() const;
97110

111+
/** \brief Returns a vector of structured erros encounted while parsing.
112+
* \return A (possibly empty) vector of StructuredError objects. Currently
113+
* only one error can be returned, but the caller should tolerate multiple
114+
* errors. This can occur if the parser recovers from a non-fatal
115+
* parse error and then encounters additional errors.
116+
*/
117+
std::vector<StructuredError> getStructuredErrors() const;
118+
98119
private:
99120
enum TokenType
100121
{

include/json/value.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,13 @@ namespace Json {
442442
iterator begin();
443443
iterator end();
444444

445+
// Accessors for the [start, limit) range of bytes within the JSON text from
446+
// which this value was parsed, if any.
447+
void setOffsetStart( size_t start );
448+
void setOffsetLimit( size_t limit );
449+
size_t getOffsetStart() const;
450+
size_t getOffsetLimit() const;
451+
445452
private:
446453
Value &resolveReference( const char *key,
447454
bool isStatic );
@@ -509,6 +516,11 @@ namespace Json {
509516
int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
510517
# endif
511518
CommentInfo *comments_;
519+
520+
// [start, limit) byte offsets in the source JSON text from which this Value
521+
// was extracted.
522+
size_t start_;
523+
size_t limit_;
512524
};
513525

514526

src/lib_json/json_reader.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,11 @@ Reader::readValue()
215215
{
216216
case tokenObjectBegin:
217217
successful = readObject( token );
218+
currentValue().setOffsetLimit(current_ - begin_);
218219
break;
219220
case tokenArrayBegin:
220221
successful = readArray( token );
222+
currentValue().setOffsetLimit(current_ - begin_);
221223
break;
222224
case tokenNumber:
223225
successful = decodeNumber( token );
@@ -227,12 +229,18 @@ Reader::readValue()
227229
break;
228230
case tokenTrue:
229231
currentValue() = true;
232+
currentValue().setOffsetStart(token.start_ - begin_);
233+
currentValue().setOffsetLimit(token.end_ - begin_);
230234
break;
231235
case tokenFalse:
232236
currentValue() = false;
237+
currentValue().setOffsetStart(token.start_ - begin_);
238+
currentValue().setOffsetLimit(token.end_ - begin_);
233239
break;
234240
case tokenNull:
235241
currentValue() = Value();
242+
currentValue().setOffsetStart(token.start_ - begin_);
243+
currentValue().setOffsetLimit(token.end_ - begin_);
236244
break;
237245
case tokenArraySeparator:
238246
if ( features_.allowDroppedNullPlaceholders_ )
@@ -241,10 +249,14 @@ Reader::readValue()
241249
// token.
242250
current_--;
243251
currentValue() = Value();
252+
currentValue().setOffsetStart(current_ - begin_ - 1);
253+
currentValue().setOffsetLimit(current_ - begin_);
244254
break;
245255
}
246256
// Else, fall through...
247257
default:
258+
currentValue().setOffsetStart(token.start_ - begin_);
259+
currentValue().setOffsetLimit(token.end_ - begin_);
248260
return addError( "Syntax error: value, object or array expected.", token );
249261
}
250262

@@ -493,11 +505,12 @@ Reader::readString()
493505

494506

495507
bool
496-
Reader::readObject( Token &/*tokenStart*/ )
508+
Reader::readObject( Token &tokenStart )
497509
{
498510
Token tokenName;
499511
std::string name;
500512
currentValue() = Value( objectValue );
513+
currentValue().setOffsetStart(tokenStart.start_ - begin_);
501514
while ( readToken( tokenName ) )
502515
{
503516
bool initialTokenOk = true;
@@ -564,9 +577,10 @@ Reader::readObject( Token &/*tokenStart*/ )
564577

565578

566579
bool
567-
Reader::readArray( Token &/*tokenStart*/ )
580+
Reader::readArray( Token &tokenStart )
568581
{
569582
currentValue() = Value( arrayValue );
583+
currentValue().setOffsetStart(tokenStart.start_ - begin_);
570584
skipSpaces();
571585
if ( *current_ == ']' ) // empty array
572586
{
@@ -613,6 +627,8 @@ Reader::decodeNumber( Token &token )
613627
if ( !decodeNumber( token, decoded ) )
614628
return false;
615629
currentValue() = decoded;
630+
currentValue().setOffsetStart(token.start_ - begin_);
631+
currentValue().setOffsetLimit(token.end_ - begin_);
616632
return true;
617633
}
618634

@@ -678,6 +694,8 @@ Reader::decodeDouble( Token &token )
678694
if ( !decodeDouble( token, decoded ) )
679695
return false;
680696
currentValue() = decoded;
697+
currentValue().setOffsetStart(token.start_ - begin_);
698+
currentValue().setOffsetLimit(token.end_ - begin_);
681699
return true;
682700
}
683701

@@ -729,6 +747,8 @@ Reader::decodeString( Token &token )
729747
if ( !decodeString( token, decoded ) )
730748
return false;
731749
currentValue() = decoded;
750+
currentValue().setOffsetStart(token.start_ - begin_);
751+
currentValue().setOffsetLimit(token.end_ - begin_);
732752
return true;
733753
}
734754

@@ -963,6 +983,25 @@ Reader::getFormattedErrorMessages() const
963983
}
964984

965985

986+
std::vector<Reader::StructuredError>
987+
Reader::getStructuredErrors() const
988+
{
989+
std::vector<Reader::StructuredError> allErrors;
990+
for ( Errors::const_iterator itError = errors_.begin();
991+
itError != errors_.end();
992+
++itError )
993+
{
994+
const ErrorInfo &error = *itError;
995+
Reader::StructuredError structured;
996+
structured.offset_start = error.token_.start_ - begin_;
997+
structured.offset_limit = error.token_.end_ - begin_;
998+
structured.message = error.message_;
999+
allErrors.push_back(structured);
1000+
}
1001+
return allErrors;
1002+
}
1003+
1004+
9661005
std::istream& operator>>( std::istream &sin, Value &root )
9671006
{
9681007
Json::Reader reader;

src/lib_json/json_value.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ Value::Value( ValueType type )
274274
, itemIsUsed_( 0 )
275275
#endif
276276
, comments_( 0 )
277+
, start_( 0 )
278+
, limit_( 0 )
277279
{
278280
switch ( type )
279281
{
@@ -318,6 +320,8 @@ Value::Value( UInt value )
318320
, itemIsUsed_( 0 )
319321
#endif
320322
, comments_( 0 )
323+
, start_( 0 )
324+
, limit_( 0 )
321325
{
322326
value_.uint_ = value;
323327
}
@@ -329,6 +333,8 @@ Value::Value( Int value )
329333
, itemIsUsed_( 0 )
330334
#endif
331335
, comments_( 0 )
336+
, start_( 0 )
337+
, limit_( 0 )
332338
{
333339
value_.int_ = value;
334340
}
@@ -342,6 +348,8 @@ Value::Value( Int64 value )
342348
, itemIsUsed_( 0 )
343349
#endif
344350
, comments_( 0 )
351+
, start_( 0 )
352+
, limit_( 0 )
345353
{
346354
value_.int_ = value;
347355
}
@@ -354,6 +362,8 @@ Value::Value( UInt64 value )
354362
, itemIsUsed_( 0 )
355363
#endif
356364
, comments_( 0 )
365+
, start_( 0 )
366+
, limit_( 0 )
357367
{
358368
value_.uint_ = value;
359369
}
@@ -366,6 +376,8 @@ Value::Value( double value )
366376
, itemIsUsed_( 0 )
367377
#endif
368378
, comments_( 0 )
379+
, start_( 0 )
380+
, limit_( 0 )
369381
{
370382
value_.real_ = value;
371383
}
@@ -377,6 +389,8 @@ Value::Value( const char *value )
377389
, itemIsUsed_( 0 )
378390
#endif
379391
, comments_( 0 )
392+
, start_( 0 )
393+
, limit_( 0 )
380394
{
381395
value_.string_ = duplicateStringValue( value );
382396
}
@@ -390,6 +404,8 @@ Value::Value( const char *beginValue,
390404
, itemIsUsed_( 0 )
391405
#endif
392406
, comments_( 0 )
407+
, start_( 0 )
408+
, limit_( 0 )
393409
{
394410
value_.string_ = duplicateStringValue( beginValue,
395411
(unsigned int)(endValue - beginValue) );
@@ -403,6 +419,8 @@ Value::Value( const std::string &value )
403419
, itemIsUsed_( 0 )
404420
#endif
405421
, comments_( 0 )
422+
, start_( 0 )
423+
, limit_( 0 )
406424
{
407425
value_.string_ = duplicateStringValue( value.c_str(),
408426
(unsigned int)value.length() );
@@ -416,6 +434,8 @@ Value::Value( const StaticString &value )
416434
, itemIsUsed_( 0 )
417435
#endif
418436
, comments_( 0 )
437+
, start_( 0 )
438+
, limit_( 0 )
419439
{
420440
value_.string_ = const_cast<char *>( value.c_str() );
421441
}
@@ -429,6 +449,8 @@ Value::Value( const CppTL::ConstString &value )
429449
, itemIsUsed_( 0 )
430450
#endif
431451
, comments_( 0 )
452+
, start_( 0 )
453+
, limit_( 0 )
432454
{
433455
value_.string_ = duplicateStringValue( value, value.length() );
434456
}
@@ -441,6 +463,8 @@ Value::Value( bool value )
441463
, itemIsUsed_( 0 )
442464
#endif
443465
, comments_( 0 )
466+
, start_( 0 )
467+
, limit_( 0 )
444468
{
445469
value_.bool_ = value;
446470
}
@@ -453,6 +477,8 @@ Value::Value( const Value &other )
453477
, itemIsUsed_( 0 )
454478
#endif
455479
, comments_( 0 )
480+
, start_( other.start_ )
481+
, limit_( other.limit_ )
456482
{
457483
switch ( type_ )
458484
{
@@ -557,6 +583,8 @@ Value::swap( Value &other )
557583
int temp2 = allocated_;
558584
allocated_ = other.allocated_;
559585
other.allocated_ = temp2;
586+
std::swap( start_, other.start_ );
587+
std::swap( limit_, other.limit_ );
560588
}
561589

562590
ValueType
@@ -1027,7 +1055,8 @@ void
10271055
Value::clear()
10281056
{
10291057
JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue || type_ == objectValue, "in Json::Value::clear(): requires complex value" );
1030-
1058+
start_ = 0;
1059+
limit_ = 0;
10311060
switch ( type_ )
10321061
{
10331062
#ifndef JSON_VALUE_USE_INTERNAL_MAP
@@ -1556,6 +1585,34 @@ Value::getComment( CommentPlacement placement ) const
15561585
}
15571586

15581587

1588+
void
1589+
Value::setOffsetStart( size_t start )
1590+
{
1591+
start_ = start;
1592+
}
1593+
1594+
1595+
void
1596+
Value::setOffsetLimit( size_t limit )
1597+
{
1598+
limit_ = limit;
1599+
}
1600+
1601+
1602+
size_t
1603+
Value::getOffsetStart() const
1604+
{
1605+
return start_;
1606+
}
1607+
1608+
1609+
size_t
1610+
Value::getOffsetLimit() const
1611+
{
1612+
return limit_;
1613+
}
1614+
1615+
15591616
std::string
15601617
Value::toStyledString() const
15611618
{

0 commit comments

Comments
 (0)