Drizzled Public API Documentation

json_reader.cpp
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2  *
3  * JSON Library, originally from http://jsoncpp.sourceforge.net/
4  *
5  * Copyright (C) 2011 Stewart Smith
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following disclaimer
17  * in the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * * The names of its contributors may not be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  */
37 
38 #include <config.h>
39 
40 #include <plugin/json_server/json/reader.h>
41 #include <plugin/json_server/json/value.h>
42 
43 #include <cassert>
44 #include <cstdio>
45 #include <cstring>
46 #include <iostream>
47 #include <stdexcept>
48 #include <utility>
49 
50 namespace Json {
51 
52 // Implementation of class Features
53 // ////////////////////////////////
54 
56  : allowComments_( true )
57  , strictRoot_( false )
58 {
59 }
60 
61 
62 Features
64 {
65  return Features();
66 }
67 
68 
69 Features
71 {
72  Features features;
73  features.allowComments_ = false;
74  features.strictRoot_ = true;
75  return features;
76 }
77 
78 // Implementation of class Reader
79 // ////////////////////////////////
80 
81 
82 static inline bool
83 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
84 {
85  return c == c1 || c == c2 || c == c3 || c == c4;
86 }
87 
88 static inline bool
89 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
90 {
91  return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
92 }
93 
94 
95 static bool
96 containsNewLine( Reader::Location begin,
97  Reader::Location end )
98 {
99  for ( ;begin < end; ++begin )
100  if ( *begin == '\n' || *begin == '\r' )
101  return true;
102  return false;
103 }
104 
105 static std::string codePointToUTF8(unsigned int cp)
106 {
107  std::string result;
108 
109  // based on description from http://en.wikipedia.org/wiki/UTF-8
110 
111  if (cp <= 0x7f)
112  {
113  result.resize(1);
114  result[0] = static_cast<char>(cp);
115  }
116  else if (cp <= 0x7FF)
117  {
118  result.resize(2);
119  result[1] = static_cast<char>(0x80 | (0x3f & cp));
120  result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
121  }
122  else if (cp <= 0xFFFF)
123  {
124  result.resize(3);
125  result[2] = static_cast<char>(0x80 | (0x3f & cp));
126  result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
127  result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
128  }
129  else if (cp <= 0x10FFFF)
130  {
131  result.resize(4);
132  result[3] = static_cast<char>(0x80 | (0x3f & cp));
133  result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
134  result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
135  result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
136  }
137 
138  return result;
139 }
140 
141 
142 // Class Reader
143 // //////////////////////////////////////////////////////////////////
144 
146  : features_( Features::all() )
147 {
148 }
149 
150 
151 Reader::Reader( const Features &features )
152  : features_( features )
153 {
154 }
155 
156 
157 bool
158 Reader::parse( const std::string &document,
159  Value &root,
160  bool collectComments )
161 {
162  document_ = document;
163  const char *begin = document_.c_str();
164  const char *end = begin + document_.length();
165  return parse( begin, end, root, collectComments );
166 }
167 
168 
169 bool
170 Reader::parse( std::istream& sin,
171  Value &root,
172  bool collectComments )
173 {
174  //std::istream_iterator<char> begin(sin);
175  //std::istream_iterator<char> end;
176  // Those would allow streamed input from a file, if parse() were a
177  // template function.
178 
179  // Since std::string is reference-counted, this at least does not
180  // create an extra copy.
181  std::string doc;
182  std::getline(sin, doc, (char)EOF);
183  return parse( doc, root, collectComments );
184 }
185 
186 bool
187 Reader::parse( const char *beginDoc, const char *endDoc,
188  Value &root,
189  bool collectComments )
190 {
191  if ( !features_.allowComments_ )
192  {
193  collectComments = false;
194  }
195 
196  begin_ = beginDoc;
197  end_ = endDoc;
198  collectComments_ = collectComments;
199  current_ = begin_;
200  lastValueEnd_ = 0;
201  lastValue_ = 0;
202  commentsBefore_ = "";
203  errors_.clear();
204  while ( !nodes_.empty() )
205  nodes_.pop();
206  nodes_.push( &root );
207 
208  bool successful = readValue();
209  Token token;
210  skipCommentTokens( token );
211  if ( collectComments_ && !commentsBefore_.empty() )
212  root.setComment( commentsBefore_, commentAfter );
213  if ( features_.strictRoot_ )
214  {
215  if ( !root.isArray() && !root.isObject() )
216  {
217  // Set error location to start of doc, ideally should be first token found in doc
218  token.type_ = tokenError;
219  token.start_ = beginDoc;
220  token.end_ = endDoc;
221  addError( "A valid JSON document must be either an array or an object value.",
222  token );
223  return false;
224  }
225  }
226  return successful;
227 }
228 
229 
230 bool
231 Reader::readValue()
232 {
233  Token token;
234  skipCommentTokens( token );
235  bool successful = true;
236 
237  if ( collectComments_ && !commentsBefore_.empty() )
238  {
239  currentValue().setComment( commentsBefore_, commentBefore );
240  commentsBefore_ = "";
241  }
242 
243 
244  switch ( token.type_ )
245  {
246  case tokenObjectBegin:
247  successful = readObject( token );
248  break;
249  case tokenArrayBegin:
250  successful = readArray( token );
251  break;
252  case tokenNumber:
253  successful = decodeNumber( token );
254  break;
255  case tokenString:
256  successful = decodeString( token );
257  break;
258  case tokenTrue:
259  currentValue() = true;
260  break;
261  case tokenFalse:
262  currentValue() = false;
263  break;
264  case tokenNull:
265  currentValue() = Value();
266  break;
267  default:
268  return addError( "Syntax error: value, object or array expected.", token );
269  }
270 
271  if ( collectComments_ )
272  {
273  lastValueEnd_ = current_;
274  lastValue_ = &currentValue();
275  }
276 
277  return successful;
278 }
279 
280 
281 void
282 Reader::skipCommentTokens( Token &token )
283 {
284  if ( features_.allowComments_ )
285  {
286  do
287  {
288  readToken( token );
289  }
290  while ( token.type_ == tokenComment );
291  }
292  else
293  {
294  readToken( token );
295  }
296 }
297 
298 
299 bool
300 Reader::expectToken( TokenType type, Token &token, const char *message )
301 {
302  readToken( token );
303  if ( token.type_ != type )
304  return addError( message, token );
305  return true;
306 }
307 
308 
309 bool
310 Reader::readToken( Token &token )
311 {
312  skipSpaces();
313  token.start_ = current_;
314  Char c = getNextChar();
315  bool ok = true;
316  switch ( c )
317  {
318  case '{':
319  token.type_ = tokenObjectBegin;
320  break;
321  case '}':
322  token.type_ = tokenObjectEnd;
323  break;
324  case '[':
325  token.type_ = tokenArrayBegin;
326  break;
327  case ']':
328  token.type_ = tokenArrayEnd;
329  break;
330  case '"':
331  token.type_ = tokenString;
332  ok = readString();
333  break;
334  case '/':
335  token.type_ = tokenComment;
336  ok = readComment();
337  break;
338  case '0':
339  case '1':
340  case '2':
341  case '3':
342  case '4':
343  case '5':
344  case '6':
345  case '7':
346  case '8':
347  case '9':
348  case '-':
349  token.type_ = tokenNumber;
350  readNumber();
351  break;
352  case 't':
353  token.type_ = tokenTrue;
354  ok = match( "rue", 3 );
355  break;
356  case 'f':
357  token.type_ = tokenFalse;
358  ok = match( "alse", 4 );
359  break;
360  case 'n':
361  token.type_ = tokenNull;
362  ok = match( "ull", 3 );
363  break;
364  case ',':
365  token.type_ = tokenArraySeparator;
366  break;
367  case ':':
368  token.type_ = tokenMemberSeparator;
369  break;
370  case 0:
371  token.type_ = tokenEndOfStream;
372  break;
373  default:
374  ok = false;
375  break;
376  }
377  if ( !ok )
378  token.type_ = tokenError;
379  token.end_ = current_;
380  return true;
381 }
382 
383 
384 void
385 Reader::skipSpaces()
386 {
387  while ( current_ != end_ )
388  {
389  Char c = *current_;
390  if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
391  ++current_;
392  else
393  break;
394  }
395 }
396 
397 
398 bool
399 Reader::match( Location pattern,
400  int patternLength )
401 {
402  if ( end_ - current_ < patternLength )
403  return false;
404  int index = patternLength;
405  while ( index-- )
406  if ( current_[index] != pattern[index] )
407  return false;
408  current_ += patternLength;
409  return true;
410 }
411 
412 
413 bool
414 Reader::readComment()
415 {
416  Location commentBegin = current_ - 1;
417  Char c = getNextChar();
418  bool successful = false;
419  if ( c == '*' )
420  successful = readCStyleComment();
421  else if ( c == '/' )
422  successful = readCppStyleComment();
423  if ( !successful )
424  return false;
425 
426  if ( collectComments_ )
427  {
428  CommentPlacement placement = commentBefore;
429  if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
430  {
431  if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
432  placement = commentAfterOnSameLine;
433  }
434 
435  addComment( commentBegin, current_, placement );
436  }
437  return true;
438 }
439 
440 
441 void
442 Reader::addComment( Location begin,
443  Location end,
444  CommentPlacement placement )
445 {
446  assert( collectComments_ );
447  if ( placement == commentAfterOnSameLine )
448  {
449  assert( lastValue_ != 0 );
450  lastValue_->setComment( std::string( begin, end ), placement );
451  }
452  else
453  {
454  if ( !commentsBefore_.empty() )
455  commentsBefore_ += "\n";
456  commentsBefore_ += std::string( begin, end );
457  }
458 }
459 
460 
461 bool
462 Reader::readCStyleComment()
463 {
464  while ( current_ != end_ )
465  {
466  Char c = getNextChar();
467  if ( c == '*' && *current_ == '/' )
468  break;
469  }
470  return getNextChar() == '/';
471 }
472 
473 
474 bool
475 Reader::readCppStyleComment()
476 {
477  while ( current_ != end_ )
478  {
479  Char c = getNextChar();
480  if ( c == '\r' || c == '\n' )
481  break;
482  }
483  return true;
484 }
485 
486 
487 void
488 Reader::readNumber()
489 {
490  while ( current_ != end_ )
491  {
492  if ( !(*current_ >= '0' && *current_ <= '9') &&
493  !in( *current_, '.', 'e', 'E', '+', '-' ) )
494  break;
495  ++current_;
496  }
497 }
498 
499 bool
500 Reader::readString()
501 {
502  Char c = 0;
503  while ( current_ != end_ )
504  {
505  c = getNextChar();
506  if ( c == '\\' )
507  getNextChar();
508  else if ( c == '"' )
509  break;
510  }
511  return c == '"';
512 }
513 
514 
515 bool
516 Reader::readObject( Token & )
517 {
518  Token tokenName;
519  std::string name;
520  currentValue() = Value( objectValue );
521  while ( readToken( tokenName ) )
522  {
523  bool initialTokenOk = true;
524  while ( tokenName.type_ == tokenComment && initialTokenOk )
525  initialTokenOk = readToken( tokenName );
526  if ( !initialTokenOk )
527  break;
528  if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
529  return true;
530  if ( tokenName.type_ != tokenString )
531  break;
532 
533  name = "";
534  if ( !decodeString( tokenName, name ) )
535  return recoverFromError( tokenObjectEnd );
536 
537  Token colon;
538  if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
539  {
540  return addErrorAndRecover( "Missing ':' after object member name",
541  colon,
542  tokenObjectEnd );
543  }
544  Value &value = currentValue()[ name ];
545  nodes_.push( &value );
546  bool ok = readValue();
547  nodes_.pop();
548  if ( !ok ) // error already set
549  return recoverFromError( tokenObjectEnd );
550 
551  Token comma;
552  if ( !readToken( comma )
553  || ( comma.type_ != tokenObjectEnd &&
554  comma.type_ != tokenArraySeparator &&
555  comma.type_ != tokenComment ) )
556  {
557  return addErrorAndRecover( "Missing ',' or '}' in object declaration",
558  comma,
559  tokenObjectEnd );
560  }
561  bool finalizeTokenOk = true;
562  while ( comma.type_ == tokenComment &&
563  finalizeTokenOk )
564  finalizeTokenOk = readToken( comma );
565  if ( comma.type_ == tokenObjectEnd )
566  return true;
567  }
568  return addErrorAndRecover( "Missing '}' or object member name",
569  tokenName,
570  tokenObjectEnd );
571 }
572 
573 
574 bool
575 Reader::readArray( Token & )
576 {
577  currentValue() = Value( arrayValue );
578  skipSpaces();
579  if ( *current_ == ']' ) // empty array
580  {
581  Token endArray;
582  readToken( endArray );
583  return true;
584  }
585  int index = 0;
586  while ( true )
587  {
588  Value &value = currentValue()[ index++ ];
589  nodes_.push( &value );
590  bool ok = readValue();
591  nodes_.pop();
592  if ( !ok ) // error already set
593  return recoverFromError( tokenArrayEnd );
594 
595  Token token;
596  // Accept Comment after last item in the array.
597  ok = readToken( token );
598  while ( token.type_ == tokenComment && ok )
599  {
600  ok = readToken( token );
601  }
602  bool badTokenType = ( token.type_ == tokenArraySeparator &&
603  token.type_ == tokenArrayEnd );
604  if ( !ok || badTokenType )
605  {
606  return addErrorAndRecover( "Missing ',' or ']' in array declaration",
607  token,
608  tokenArrayEnd );
609  }
610  if ( token.type_ == tokenArrayEnd )
611  break;
612  }
613  return true;
614 }
615 
616 
617 bool
618 Reader::decodeNumber( Token &token )
619 {
620  bool isDouble = false;
621  for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
622  {
623  isDouble = isDouble
624  || in( *inspect, '.', 'e', 'E', '+' )
625  || ( *inspect == '-' && inspect != token.start_ );
626  }
627  if ( isDouble )
628  return decodeDouble( token );
629  Location current = token.start_;
630  bool isNegative = *current == '-';
631  if ( isNegative )
632  ++current;
633  Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt)
634  : Value::maxUInt) / 10;
635  Value::UInt value = 0;
636  while ( current < token.end_ )
637  {
638  Char c = *current++;
639  if ( c < '0' || c > '9' )
640  return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
641  if ( value >= threshold )
642  return decodeDouble( token );
643  value = value * 10 + Value::UInt(c - '0');
644  }
645  if ( isNegative )
646  currentValue() = -Value::Int( value );
647  else if ( value <= Value::UInt(Value::maxInt) )
648  currentValue() = Value::Int( value );
649  else
650  currentValue() = value;
651  return true;
652 }
653 
654 
655 bool
656 Reader::decodeDouble( Token &token )
657 {
658  double value = 0;
659  const int bufferSize = 32;
660  int count;
661  int length = int(token.end_ - token.start_);
662  if ( length <= bufferSize )
663  {
664  Char buffer[bufferSize];
665  memcpy( buffer, token.start_, length );
666  buffer[length] = 0;
667  count = sscanf( buffer, "%lf", &value );
668  }
669  else
670  {
671  std::string buffer( token.start_, token.end_ );
672  count = sscanf( buffer.c_str(), "%lf", &value );
673  }
674 
675  if ( count != 1 )
676  return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
677  currentValue() = value;
678  return true;
679 }
680 
681 
682 bool
683 Reader::decodeString( Token &token )
684 {
685  std::string decoded;
686  if ( !decodeString( token, decoded ) )
687  return false;
688  currentValue() = decoded;
689  return true;
690 }
691 
692 
693 bool
694 Reader::decodeString( Token &token, std::string &decoded )
695 {
696  decoded.reserve( token.end_ - token.start_ - 2 );
697  Location current = token.start_ + 1; // skip '"'
698  Location end = token.end_ - 1; // do not include '"'
699  while ( current != end )
700  {
701  Char c = *current++;
702  if ( c == '"' )
703  break;
704  else if ( c == '\\' )
705  {
706  if ( current == end )
707  return addError( "Empty escape sequence in string", token, current );
708  Char escape = *current++;
709  switch ( escape )
710  {
711  case '"': decoded += '"'; break;
712  case '/': decoded += '/'; break;
713  case '\\': decoded += '\\'; break;
714  case 'b': decoded += '\b'; break;
715  case 'f': decoded += '\f'; break;
716  case 'n': decoded += '\n'; break;
717  case 'r': decoded += '\r'; break;
718  case 't': decoded += '\t'; break;
719  case 'u':
720  {
721  unsigned int unicode;
722  if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
723  return false;
724  decoded += codePointToUTF8(unicode);
725  }
726  break;
727  default:
728  return addError( "Bad escape sequence in string", token, current );
729  }
730  }
731  else
732  {
733  decoded += c;
734  }
735  }
736  return true;
737 }
738 
739 bool
740 Reader::decodeUnicodeCodePoint( Token &token,
741  Location &current,
742  Location end,
743  unsigned int &unicode )
744 {
745 
746  if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
747  return false;
748  if (unicode >= 0xD800 && unicode <= 0xDBFF)
749  {
750  // surrogate pairs
751  if (end - current < 6)
752  return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
753  unsigned int surrogatePair;
754  if (*(current++) == '\\' && *(current++)== 'u')
755  {
756  if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
757  {
758  unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
759  }
760  else
761  return false;
762  }
763  else
764  return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
765  }
766  return true;
767 }
768 
769 bool
770 Reader::decodeUnicodeEscapeSequence( Token &token,
771  Location &current,
772  Location end,
773  unsigned int &unicode )
774 {
775  if ( end - current < 4 )
776  return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
777  unicode = 0;
778  for ( int index =0; index < 4; ++index )
779  {
780  Char c = *current++;
781  unicode *= 16;
782  if ( c >= '0' && c <= '9' )
783  unicode += c - '0';
784  else if ( c >= 'a' && c <= 'f' )
785  unicode += c - 'a' + 10;
786  else if ( c >= 'A' && c <= 'F' )
787  unicode += c - 'A' + 10;
788  else
789  return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
790  }
791  return true;
792 }
793 
794 
795 bool
796 Reader::addError( const std::string &message,
797  Token &token,
798  Location extra )
799 {
800  ErrorInfo info;
801  info.token_ = token;
802  info.message_ = message;
803  info.extra_ = extra;
804  errors_.push_back( info );
805  return false;
806 }
807 
808 
809 bool
810 Reader::recoverFromError( TokenType skipUntilToken )
811 {
812  int errorCount = int(errors_.size());
813  Token skip;
814  while ( true )
815  {
816  if ( !readToken(skip) )
817  errors_.resize( errorCount ); // discard errors caused by recovery
818  if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
819  break;
820  }
821  errors_.resize( errorCount );
822  return false;
823 }
824 
825 
826 bool
827 Reader::addErrorAndRecover( const std::string &message,
828  Token &token,
829  TokenType skipUntilToken )
830 {
831  addError( message, token );
832  return recoverFromError( skipUntilToken );
833 }
834 
835 
836 Value &
837 Reader::currentValue()
838 {
839  return *(nodes_.top());
840 }
841 
842 
843 Reader::Char
844 Reader::getNextChar()
845 {
846  if ( current_ == end_ )
847  return 0;
848  return *current_++;
849 }
850 
851 
852 void
853 Reader::getLocationLineAndColumn( Location location,
854  int &line,
855  int &column ) const
856 {
857  Location current = begin_;
858  Location lastLineStart = current;
859  line = 0;
860  while ( current < location && current != end_ )
861  {
862  Char c = *current++;
863  if ( c == '\r' )
864  {
865  if ( *current == '\n' )
866  ++current;
867  lastLineStart = current;
868  ++line;
869  }
870  else if ( c == '\n' )
871  {
872  lastLineStart = current;
873  ++line;
874  }
875  }
876  // column & line start at 1
877  column = int(location - lastLineStart) + 1;
878  ++line;
879 }
880 
881 
882 std::string
883 Reader::getLocationLineAndColumn( Location location ) const
884 {
885  int line, column;
886  getLocationLineAndColumn( location, line, column );
887  char buffer[18+16+16+1];
888  sprintf( buffer, "Line %d, Column %d", line, column );
889  return buffer;
890 }
891 
892 
893 std::string
895 {
896  std::string formattedMessage;
897  for ( Errors::const_iterator itError = errors_.begin();
898  itError != errors_.end();
899  ++itError )
900  {
901  const ErrorInfo &error = *itError;
902  formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
903  formattedMessage += " " + error.message_ + "\n";
904  if ( error.extra_ )
905  formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
906  }
907  return formattedMessage;
908 }
909 
910 
911 std::istream& operator>>( std::istream &sin, Value &root )
912 {
913  Json::Reader reader;
914  bool ok = reader.parse(sin, root, true);
915  //JSON_ASSERT( ok );
916  if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
917  return sin;
918 }
919 
920 
921 } // namespace Json
bool allowComments_
true if comments are allowed. Default: true.
Definition: features.h:72
Features()
Initialize the configuration like JsonConfig::allFeatures;.
Definition: json_reader.cpp:55
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
Represents a JSON value.
Definition: value.h:149
void setComment(const char *comment, CommentPlacement placement)
Comments must be //... or /* ... */.
Configuration passed to reader and writer. This configuration object can be used to force the Reader ...
Definition: features.h:50
a comment placed on the line before a value
Definition: value.h:75
Reader()
Constructs a Reader allowing all features for parsing.
object value (collection of name/value pairs).
Definition: value.h:70
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
Definition: json_reader.cpp:63
JSON (JavaScript Object Notation).
Definition: features.h:44
a comment just after a value on the same line
Definition: value.h:76
std::istream & operator>>(std::istream &sin, Value &root)
Read from 'sin' into 'root'.
CommentPlacement
Definition: value.h:73
a comment on the line after a value (only make sense for root value)
Definition: value.h:77
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
Definition: json_reader.cpp:70
Unserialize a JSON document into a Value.
Definition: reader.h:52
bool strictRoot_
true if root must be either an array or an object value. Default: false.
Definition: features.h:75
std::string getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
array value (ordered list)
Definition: value.h:69