JsonCpp project page JsonCpp home page

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

SourceForge Logo hosts this site. Send comments to:
Json-cpp Developers