7#if !defined(JSON_IS_AMALGAMATION)
29#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
30#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
36#pragma warning(disable : 4996)
41#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
42#define JSONCPP_DEPRECATED_STACK_LIMIT 256
72 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
83 bool collectComments) {
84 document_.assign(document.begin(), document.end());
85 const char* begin = document_.c_str();
86 const char* end = begin + document_.length();
87 return parse(begin, end, root, collectComments);
91 document_.assign(std::istreambuf_iterator<char>(is),
92 std::istreambuf_iterator<char>());
93 return parse(document_.data(), document_.data() + document_.size(), root,
98 bool collectComments) {
99 if (!features_.allowComments_) {
100 collectComments =
false;
105 collectComments_ = collectComments;
107 lastValueEnd_ =
nullptr;
108 lastValue_ =
nullptr;
109 commentsBefore_.clear();
111 while (!nodes_.empty())
115 bool successful = readValue();
117 readTokenSkippingComments(token);
118 if (collectComments_ && !commentsBefore_.empty())
120 if (features_.strictRoot_) {
124 token.type_ = tokenError;
125 token.start_ = beginDoc;
128 "A valid JSON document must be either an array or an object value.",
136bool Reader::readValue() {
142#if JSON_USE_EXCEPTION
150 readTokenSkippingComments(token);
151 bool successful =
true;
153 if (collectComments_ && !commentsBefore_.empty()) {
155 commentsBefore_.clear();
158 switch (token.type_) {
159 case tokenObjectBegin:
160 successful = readObject(token);
163 case tokenArrayBegin:
164 successful = readArray(token);
168 successful = decodeNumber(token);
171 successful = decodeString(token);
181 currentValue().swapPayload(v);
182 currentValue().setOffsetStart(token.start_ - begin_);
183 currentValue().setOffsetLimit(token.end_ - begin_);
187 currentValue().swapPayload(v);
188 currentValue().setOffsetStart(token.start_ - begin_);
189 currentValue().setOffsetLimit(token.end_ - begin_);
191 case tokenArraySeparator:
194 if (features_.allowDroppedNullPlaceholders_) {
199 currentValue().swapPayload(v);
200 currentValue().setOffsetStart(current_ - begin_ - 1);
201 currentValue().setOffsetLimit(current_ - begin_);
205 currentValue().setOffsetStart(token.start_ - begin_);
206 currentValue().setOffsetLimit(token.end_ - begin_);
207 return addError(
"Syntax error: value, object or array expected.", token);
210 if (collectComments_) {
211 lastValueEnd_ = current_;
212 lastValue_ = ¤tValue();
218bool Reader::readTokenSkippingComments(Token& token) {
219 bool success = readToken(token);
220 if (features_.allowComments_) {
221 while (success && token.type_ == tokenComment) {
222 success = readToken(token);
228bool Reader::readToken(Token& token) {
230 token.start_ = current_;
231 Char c = getNextChar();
235 token.type_ = tokenObjectBegin;
238 token.type_ = tokenObjectEnd;
241 token.type_ = tokenArrayBegin;
244 token.type_ = tokenArrayEnd;
247 token.type_ = tokenString;
251 token.type_ = tokenComment;
265 token.type_ = tokenNumber;
269 token.type_ = tokenTrue;
270 ok = match(
"rue", 3);
273 token.type_ = tokenFalse;
274 ok = match(
"alse", 4);
277 token.type_ = tokenNull;
278 ok = match(
"ull", 3);
281 token.type_ = tokenArraySeparator;
284 token.type_ = tokenMemberSeparator;
287 token.type_ = tokenEndOfStream;
294 token.type_ = tokenError;
295 token.end_ = current_;
299void Reader::skipSpaces() {
300 while (current_ != end_) {
302 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
309bool Reader::match(
const Char* pattern,
int patternLength) {
310 if (end_ - current_ < patternLength)
312 int index = patternLength;
314 if (current_[index] != pattern[index])
316 current_ += patternLength;
320bool Reader::readComment() {
321 Location commentBegin = current_ - 1;
322 Char c = getNextChar();
323 bool successful =
false;
325 successful = readCStyleComment();
327 successful = readCppStyleComment();
331 if (collectComments_) {
333 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
334 if (c !=
'*' || !containsNewLine(commentBegin, current_))
338 addComment(commentBegin, current_, placement);
345 normalized.reserve(
static_cast<size_t>(end - begin));
347 while (current != end) {
350 if (current != end && *current ==
'\n')
362void Reader::addComment(Location begin, Location end,
364 assert(collectComments_);
365 const String& normalized = normalizeEOL(begin, end);
367 assert(lastValue_ !=
nullptr);
368 lastValue_->setComment(normalized, placement);
370 commentsBefore_ += normalized;
374bool Reader::readCStyleComment() {
375 while ((current_ + 1) < end_) {
376 Char c = getNextChar();
377 if (c ==
'*' && *current_ ==
'/')
380 return getNextChar() ==
'/';
383bool Reader::readCppStyleComment() {
384 while (current_ != end_) {
385 Char c = getNextChar();
390 if (current_ != end_ && *current_ ==
'\n')
399void Reader::readNumber() {
403 while (c >=
'0' && c <=
'9')
404 c = (current_ = p) < end_ ? *p++ :
'\0';
407 c = (current_ = p) < end_ ? *p++ :
'\0';
408 while (c >=
'0' && c <=
'9')
409 c = (current_ = p) < end_ ? *p++ :
'\0';
412 if (c ==
'e' || c ==
'E') {
413 c = (current_ = p) < end_ ? *p++ :
'\0';
414 if (c ==
'+' || c ==
'-')
415 c = (current_ = p) < end_ ? *p++ :
'\0';
416 while (c >=
'0' && c <=
'9')
417 c = (current_ = p) < end_ ? *p++ :
'\0';
421bool Reader::readString() {
423 while (current_ != end_) {
433bool Reader::readObject(Token& token) {
437 currentValue().swapPayload(init);
438 currentValue().setOffsetStart(token.start_ - begin_);
439 while (readTokenSkippingComments(tokenName)) {
440 if (tokenName.type_ == tokenObjectEnd && name.empty())
443 if (tokenName.type_ == tokenString) {
444 if (!decodeString(tokenName, name))
445 return recoverFromError(tokenObjectEnd);
446 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
448 if (!decodeNumber(tokenName, numberName))
449 return recoverFromError(tokenObjectEnd);
450 name = numberName.asString();
456 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
457 return addErrorAndRecover(
"Missing ':' after object member name", colon,
460 Value& value = currentValue()[name];
462 bool ok = readValue();
465 return recoverFromError(tokenObjectEnd);
468 if (!readTokenSkippingComments(comma) ||
469 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
470 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
471 comma, tokenObjectEnd);
473 if (comma.type_ == tokenObjectEnd)
476 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
480bool Reader::readArray(Token& token) {
482 currentValue().swapPayload(init);
483 currentValue().setOffsetStart(token.start_ - begin_);
485 if (current_ != end_ && *current_ ==
']')
493 Value& value = currentValue()[index++];
495 bool ok = readValue();
498 return recoverFromError(tokenArrayEnd);
502 ok = readTokenSkippingComments(currentToken);
503 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
504 currentToken.type_ != tokenArrayEnd);
505 if (!ok || badTokenType) {
506 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
507 currentToken, tokenArrayEnd);
509 if (currentToken.type_ == tokenArrayEnd)
515bool Reader::decodeNumber(Token& token) {
517 if (!decodeNumber(token, decoded))
519 currentValue().swapPayload(decoded);
520 currentValue().setOffsetStart(token.start_ - begin_);
521 currentValue().setOffsetLimit(token.end_ - begin_);
525bool Reader::decodeNumber(Token& token,
Value& decoded) {
530 bool isNegative = *current ==
'-';
540 while (current < token.end_) {
542 if (c <
'0' || c >
'9')
543 return decodeDouble(token, decoded);
545 if (value >= threshold) {
550 if (value > threshold || current != token.end_ ||
551 digit > maxIntegerValue % 10) {
552 return decodeDouble(token, decoded);
555 value = value * 10 + digit;
557 if (isNegative && value == maxIntegerValue)
568bool Reader::decodeDouble(Token& token) {
570 if (!decodeDouble(token, decoded))
572 currentValue().swapPayload(decoded);
573 currentValue().setOffsetStart(token.start_ - begin_);
574 currentValue().setOffsetLimit(token.end_ - begin_);
578bool Reader::decodeDouble(Token& token,
Value& decoded) {
581 is.imbue(std::locale::classic());
582 if (!(is >> value)) {
583 if (value == std::numeric_limits<double>::max())
584 value = std::numeric_limits<double>::infinity();
585 else if (value == std::numeric_limits<double>::lowest())
586 value = -std::numeric_limits<double>::infinity();
593 else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
595 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
601bool Reader::decodeString(Token& token) {
603 if (!decodeString(token, decoded_string))
605 Value decoded(decoded_string);
606 currentValue().swapPayload(decoded);
607 currentValue().setOffsetStart(token.start_ - begin_);
608 currentValue().setOffsetLimit(token.end_ - begin_);
612bool Reader::decodeString(Token& token,
String& decoded) {
613 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
614 Location current = token.start_ + 1;
616 while (current != end) {
622 return addError(
"Empty escape sequence in string", token, current);
623 Char escape = *current++;
650 unsigned int unicode;
651 if (!decodeUnicodeCodePoint(token, current, end, unicode))
653 decoded += codePointToUTF8(unicode);
656 return addError(
"Bad escape sequence in string", token, current);
659 if (
static_cast<unsigned char>(c) < 0x20)
660 return addError(
"Control character in string", token, current - 1);
667bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
668 Location end,
unsigned int& unicode) {
670 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
672 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
674 if (end - current < 6)
676 "additional six characters expected to parse unicode surrogate pair.",
678 if (*(current++) ==
'\\' && *(current++) ==
'u') {
679 unsigned int surrogatePair;
680 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
681 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
685 return addError(
"expecting another \\u token to begin the second half of "
686 "a unicode surrogate pair",
692bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
694 unsigned int& ret_unicode) {
695 if (end - current < 4)
697 "Bad unicode escape sequence in string: four digits expected.", token,
700 for (
int index = 0; index < 4; ++index) {
703 if (c >=
'0' && c <=
'9')
705 else if (c >=
'a' && c <=
'f')
706 unicode += c -
'a' + 10;
707 else if (c >=
'A' && c <=
'F')
708 unicode += c -
'A' + 10;
711 "Bad unicode escape sequence in string: hexadecimal digit expected.",
714 ret_unicode =
static_cast<unsigned int>(unicode);
718bool Reader::addError(
const String& message, Token& token, Location extra) {
721 info.message_ = message;
723 errors_.push_back(info);
727bool Reader::recoverFromError(TokenType skipUntilToken) {
728 size_t const errorCount = errors_.size();
731 if (!readToken(skip))
732 errors_.resize(errorCount);
733 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
736 errors_.resize(errorCount);
740bool Reader::addErrorAndRecover(
const String& message, Token& token,
741 TokenType skipUntilToken) {
742 addError(message, token);
743 return recoverFromError(skipUntilToken);
746Value& Reader::currentValue() {
return *(nodes_.top()); }
749 if (current_ == end_)
754void Reader::getLocationLineAndColumn(Location location,
int& line,
759 while (current < location && current != end_) {
762 if (current != end_ && *current ==
'\n')
764 lastLineStart = current;
766 }
else if (c ==
'\n') {
767 lastLineStart = current;
772 column = int(location - lastLineStart) + 1;
776String Reader::getLocationLineAndColumn(Location location)
const {
778 getLocationLineAndColumn(location, line, column);
779 char buffer[18 + 16 + 16 + 1];
780 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
791 for (
const auto& error : errors_) {
793 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
794 formattedMessage +=
" " + error.message_ +
"\n";
797 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
799 return formattedMessage;
803 std::vector<Reader::StructuredError> allErrors;
804 for (
const auto& error : errors_) {
808 structured.
message = error.message_;
809 allErrors.push_back(structured);
815 ptrdiff_t
const length = end_ - begin_;
819 token.type_ = tokenError;
824 info.message_ = message;
825 info.extra_ =
nullptr;
826 errors_.push_back(info);
831 const Value& extra) {
832 ptrdiff_t
const length = end_ - begin_;
837 token.type_ = tokenError;
842 info.message_ = message;
844 errors_.push_back(info);
854 static OurFeatures all();
856 bool allowTrailingCommas_;
858 bool allowDroppedNullPlaceholders_;
859 bool allowNumericKeys_;
860 bool allowSingleQuotes_;
863 bool allowSpecialFloats_;
868OurFeatures OurFeatures::all() {
return {}; }
878 using Location =
const Char*;
880 explicit OurReader(OurFeatures
const& features);
881 bool parse(
const char* beginDoc,
const char* endDoc, Value& root,
882 bool collectComments =
true);
883 String getFormattedErrorMessages()
const;
884 std::vector<CharReader::StructuredError> getStructuredErrors()
const;
887 OurReader(OurReader
const&);
888 void operator=(OurReader
const&);
891 tokenEndOfStream = 0,
905 tokenMemberSeparator,
924 using Errors = std::deque<ErrorInfo>;
926 bool readToken(Token& token);
927 bool readTokenSkippingComments(Token& token);
929 void skipCommentTokens();
930 void skipBom(
bool skipBom);
931 bool match(
const Char* pattern,
int patternLength);
933 bool readCStyleComment(
bool* containsNewLineResult);
934 bool readCppStyleComment();
936 bool readStringSingleQuote();
937 bool readNumber(
bool checkInf);
939 bool readObject(Token& token);
940 bool readArray(Token& token);
941 bool decodeNumber(Token& token);
942 bool decodeNumber(Token& token, Value& decoded);
943 bool decodeString(Token& token);
944 bool decodeString(Token& token,
String& decoded);
945 bool decodeDouble(Token& token);
946 bool decodeDouble(Token& token, Value& decoded);
947 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
948 unsigned int& unicode);
949 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
950 Location end,
unsigned int& unicode);
951 bool addError(
const String& message, Token& token, Location extra =
nullptr);
952 bool recoverFromError(TokenType skipUntilToken);
953 bool addErrorAndRecover(
const String& message, Token& token,
954 TokenType skipUntilToken);
955 void skipUntilSpace();
956 Value& currentValue();
958 void getLocationLineAndColumn(Location location,
int& line,
960 String getLocationLineAndColumn(Location location)
const;
963 static String normalizeEOL(Location begin, Location end);
964 static bool containsNewLine(Location begin, Location end);
966 using Nodes = std::stack<Value*>;
971 Location begin_ =
nullptr;
972 Location end_ =
nullptr;
973 Location current_ =
nullptr;
974 Location lastValueEnd_ =
nullptr;
975 Value* lastValue_ =
nullptr;
976 bool lastValueHasAComment_ =
false;
979 OurFeatures
const features_;
980 bool collectComments_ =
false;
993 static thread_local size_t count = 0;
997bool OurReader::containsNewLine(OurReader::Location begin,
998 OurReader::Location end) {
1000 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
1003OurReader::OurReader(OurFeatures
const& features) : features_(features) {}
1005bool OurReader::parse(
const char* beginDoc,
const char* endDoc, Value& root,
1006 bool collectComments) {
1007 if (!features_.allowComments_) {
1008 collectComments =
false;
1013 collectComments_ = collectComments;
1015 lastValueEnd_ =
nullptr;
1016 lastValue_ =
nullptr;
1017 commentsBefore_.clear();
1019 while (!nodes_.empty())
1024 skipBom(features_.skipBom_);
1025 bool successful = readValue();
1028 readTokenSkippingComments(token);
1029 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1030 addError(
"Extra non-whitespace after JSON value.", token);
1033 if (collectComments_ && !commentsBefore_.empty())
1034 root.setComment(commentsBefore_, commentAfter);
1035 if (features_.strictRoot_) {
1036 if (!root.isArray() && !root.isObject()) {
1039 token.type_ = tokenError;
1040 token.start_ = beginDoc;
1041 token.end_ = endDoc;
1043 "A valid JSON document must be either an array or an object value.",
1051bool OurReader::readValue() {
1053 if (nodes_.size() > features_.stackLimit_) {
1054#if JSON_USE_EXCEPTION
1058 token.start_ = current_;
1059 token.end_ = current_;
1060 token.type_ = tokenError;
1062 "Exceeded stackLimit for nested object and/or array values.", token);
1065 readTokenSkippingComments(token);
1066 bool successful =
true;
1068 if (collectComments_ && !commentsBefore_.empty()) {
1069 currentValue().setComment(commentsBefore_, commentBefore);
1070 commentsBefore_.clear();
1073 switch (token.type_) {
1074 case tokenObjectBegin:
1075 successful = readObject(token);
1076 currentValue().setOffsetLimit(current_ - begin_);
1078 case tokenArrayBegin:
1079 successful = readArray(token);
1080 currentValue().setOffsetLimit(current_ - begin_);
1083 successful = decodeNumber(token);
1086 successful = decodeString(token);
1090 currentValue().swapPayload(v);
1091 currentValue().setOffsetStart(token.start_ - begin_);
1092 currentValue().setOffsetLimit(token.end_ - begin_);
1096 currentValue().swapPayload(v);
1097 currentValue().setOffsetStart(token.start_ - begin_);
1098 currentValue().setOffsetLimit(token.end_ - begin_);
1102 currentValue().swapPayload(v);
1103 currentValue().setOffsetStart(token.start_ - begin_);
1104 currentValue().setOffsetLimit(token.end_ - begin_);
1107 Value v(std::numeric_limits<double>::quiet_NaN());
1108 currentValue().swapPayload(v);
1109 currentValue().setOffsetStart(token.start_ - begin_);
1110 currentValue().setOffsetLimit(token.end_ - begin_);
1113 Value v(std::numeric_limits<double>::infinity());
1114 currentValue().swapPayload(v);
1115 currentValue().setOffsetStart(token.start_ - begin_);
1116 currentValue().setOffsetLimit(token.end_ - begin_);
1119 Value v(-std::numeric_limits<double>::infinity());
1120 currentValue().swapPayload(v);
1121 currentValue().setOffsetStart(token.start_ - begin_);
1122 currentValue().setOffsetLimit(token.end_ - begin_);
1124 case tokenArraySeparator:
1125 case tokenObjectEnd:
1127 if (features_.allowDroppedNullPlaceholders_) {
1132 currentValue().swapPayload(v);
1133 currentValue().setOffsetStart(current_ - begin_ - 1);
1134 currentValue().setOffsetLimit(current_ - begin_);
1138 currentValue().setOffsetStart(token.start_ - begin_);
1139 currentValue().setOffsetLimit(token.end_ - begin_);
1140 return addError(
"Syntax error: value, object or array expected.", token);
1143 if (collectComments_) {
1144 lastValueEnd_ = current_;
1145 lastValueHasAComment_ =
false;
1146 lastValue_ = ¤tValue();
1152bool OurReader::readTokenSkippingComments(Token& token) {
1153 bool success = readToken(token);
1154 if (features_.allowComments_) {
1155 while (success && token.type_ == tokenComment) {
1156 success = readToken(token);
1162bool OurReader::readToken(Token& token) {
1164 token.start_ = current_;
1165 Char c = getNextChar();
1169 token.type_ = tokenObjectBegin;
1172 token.type_ = tokenObjectEnd;
1175 token.type_ = tokenArrayBegin;
1178 token.type_ = tokenArrayEnd;
1181 token.type_ = tokenString;
1185 if (features_.allowSingleQuotes_) {
1186 token.type_ = tokenString;
1187 ok = readStringSingleQuote();
1194 token.type_ = tokenComment;
1207 token.type_ = tokenNumber;
1211 if (readNumber(
true)) {
1212 token.type_ = tokenNumber;
1214 token.type_ = tokenNegInf;
1215 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1219 if (readNumber(
true)) {
1220 token.type_ = tokenNumber;
1222 token.type_ = tokenPosInf;
1223 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1227 token.type_ = tokenTrue;
1228 ok = match(
"rue", 3);
1231 token.type_ = tokenFalse;
1232 ok = match(
"alse", 4);
1235 token.type_ = tokenNull;
1236 ok = match(
"ull", 3);
1239 if (features_.allowSpecialFloats_) {
1240 token.type_ = tokenNaN;
1241 ok = match(
"aN", 2);
1247 if (features_.allowSpecialFloats_) {
1248 token.type_ = tokenPosInf;
1249 ok = match(
"nfinity", 7);
1255 token.type_ = tokenArraySeparator;
1258 token.type_ = tokenMemberSeparator;
1261 token.type_ = tokenEndOfStream;
1268 token.type_ = tokenError;
1269 token.end_ = current_;
1273void OurReader::skipSpaces() {
1274 while (current_ != end_) {
1276 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
1288void OurReader::skipCommentTokens() {
1290 if (!features_.allowComments_)
1292 while (current_ != end_ && *current_ ==
'/' && (current_ + 1) != end_ &&
1293 (current_[1] ==
'/' || current_[1] ==
'*')) {
1295 if (!readToken(comment))
1301void OurReader::skipBom(
bool skipBom) {
1304 if ((end_ - begin_) >= 3 && strncmp(begin_,
"\xEF\xBB\xBF", 3) == 0) {
1311bool OurReader::match(
const Char* pattern,
int patternLength) {
1312 if (end_ - current_ < patternLength)
1314 int index = patternLength;
1316 if (current_[index] != pattern[index])
1318 current_ += patternLength;
1322bool OurReader::readComment() {
1323 const Location commentBegin = current_ - 1;
1324 const Char c = getNextChar();
1325 bool successful =
false;
1326 bool cStyleWithEmbeddedNewline =
false;
1328 const bool isCStyleComment = (c ==
'*');
1329 const bool isCppStyleComment = (c ==
'/');
1330 if (isCStyleComment) {
1331 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1332 }
else if (isCppStyleComment) {
1333 successful = readCppStyleComment();
1339 if (collectComments_) {
1342 if (!lastValueHasAComment_) {
1343 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1344 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1352 lastValueHasAComment_ =
true;
1355 addComment(commentBegin, current_, placement);
1360String OurReader::normalizeEOL(OurReader::Location begin,
1361 OurReader::Location end) {
1363 normalized.reserve(
static_cast<size_t>(end - begin));
1364 OurReader::Location current = begin;
1365 while (current != end) {
1366 char c = *current++;
1368 if (current != end && *current ==
'\n')
1380void OurReader::addComment(Location begin, Location end,
1381 CommentPlacement placement) {
1382 assert(collectComments_);
1383 const String& normalized = normalizeEOL(begin, end);
1384 if (placement == commentAfterOnSameLine) {
1385 assert(lastValue_ !=
nullptr);
1386 lastValue_->setComment(normalized, placement);
1388 commentsBefore_ += normalized;
1392bool OurReader::readCStyleComment(
bool* containsNewLineResult) {
1393 *containsNewLineResult =
false;
1395 while ((current_ + 1) < end_) {
1396 Char c = getNextChar();
1397 if (c ==
'*' && *current_ ==
'/')
1400 *containsNewLineResult =
true;
1403 return getNextChar() ==
'/';
1406bool OurReader::readCppStyleComment() {
1407 while (current_ != end_) {
1408 Char c = getNextChar();
1413 if (current_ != end_ && *current_ ==
'\n')
1422bool OurReader::readNumber(
bool checkInf) {
1423 Location p = current_;
1424 if (checkInf && p != end_ && *p ==
'I') {
1430 while (c >=
'0' && c <=
'9')
1431 c = (current_ = p) < end_ ? *p++ :
'\0';
1434 c = (current_ = p) < end_ ? *p++ :
'\0';
1435 while (c >=
'0' && c <=
'9')
1436 c = (current_ = p) < end_ ? *p++ :
'\0';
1439 if (c ==
'e' || c ==
'E') {
1440 c = (current_ = p) < end_ ? *p++ :
'\0';
1441 if (c ==
'+' || c ==
'-')
1442 c = (current_ = p) < end_ ? *p++ :
'\0';
1443 while (c >=
'0' && c <=
'9')
1444 c = (current_ = p) < end_ ? *p++ :
'\0';
1448bool OurReader::readString() {
1450 while (current_ != end_) {
1460bool OurReader::readStringSingleQuote() {
1462 while (current_ != end_) {
1472bool OurReader::readObject(Token& token) {
1475 Value init(objectValue);
1476 currentValue().swapPayload(init);
1477 currentValue().setOffsetStart(token.start_ - begin_);
1478 while (readTokenSkippingComments(tokenName)) {
1479 if (tokenName.type_ == tokenObjectEnd &&
1481 features_.allowTrailingCommas_))
1484 if (tokenName.type_ == tokenString) {
1485 if (!decodeString(tokenName, name))
1486 return recoverFromError(tokenObjectEnd);
1487 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1489 if (!decodeNumber(tokenName, numberName))
1490 return recoverFromError(tokenObjectEnd);
1491 name = numberName.asString();
1495 if (name.length() >= (1U << 30))
1497 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1498 String msg =
"Duplicate key: '" + name +
"'";
1499 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1503 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1504 return addErrorAndRecover(
"Missing ':' after object member name", colon,
1507 Value& value = currentValue()[name];
1508 nodes_.push(&value);
1509 bool ok = readValue();
1512 return recoverFromError(tokenObjectEnd);
1515 if (!readTokenSkippingComments(comma) ||
1516 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
1517 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
1518 comma, tokenObjectEnd);
1520 if (comma.type_ == tokenObjectEnd)
1523 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
1527bool OurReader::readArray(Token& token) {
1528 Value init(arrayValue);
1529 currentValue().swapPayload(init);
1530 currentValue().setOffsetStart(token.start_ - begin_);
1536 skipCommentTokens();
1537 if (current_ != end_ && *current_ ==
']' &&
1539 (features_.allowTrailingCommas_ &&
1540 !features_.allowDroppedNullPlaceholders_)))
1544 readToken(endArray);
1547 Value& value = currentValue()[index++];
1548 nodes_.push(&value);
1549 bool ok = readValue();
1552 return recoverFromError(tokenArrayEnd);
1556 ok = readTokenSkippingComments(currentToken);
1557 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1558 currentToken.type_ != tokenArrayEnd);
1559 if (!ok || badTokenType) {
1560 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
1561 currentToken, tokenArrayEnd);
1563 if (currentToken.type_ == tokenArrayEnd)
1569bool OurReader::decodeNumber(Token& token) {
1571 if (!decodeNumber(token, decoded))
1573 currentValue().swapPayload(decoded);
1574 currentValue().setOffsetStart(token.start_ - begin_);
1575 currentValue().setOffsetLimit(token.end_ - begin_);
1579bool OurReader::decodeNumber(Token& token, Value& decoded) {
1583 Location current = token.start_;
1584 const bool isNegative = *current ==
'-';
1592 static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1593 "Int must be smaller than UInt");
1599 static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1600 "The absolute value of minLargestInt must be greater than or "
1601 "equal to maxLargestInt");
1602 static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1603 "The absolute value of minLargestInt must be only 1 magnitude "
1604 "larger than maxLargest Int");
1606 static constexpr Value::LargestUInt positive_threshold =
1607 Value::maxLargestUInt / 10;
1608 static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1615 static constexpr auto negative_threshold =
1616 Value::LargestUInt(-(Value::minLargestInt / 10));
1617 static constexpr auto negative_last_digit =
1618 Value::UInt(-(Value::minLargestInt % 10));
1620 const Value::LargestUInt threshold =
1621 isNegative ? negative_threshold : positive_threshold;
1622 const Value::UInt max_last_digit =
1623 isNegative ? negative_last_digit : positive_last_digit;
1625 Value::LargestUInt value = 0;
1626 while (current < token.end_) {
1627 Char c = *current++;
1628 if (c <
'0' || c >
'9')
1629 return decodeDouble(token, decoded);
1631 const auto digit(
static_cast<Value::UInt
>(c -
'0'));
1632 if (value >= threshold) {
1638 if (value > threshold || current != token.end_ ||
1639 digit > max_last_digit) {
1640 return decodeDouble(token, decoded);
1643 value = value * 10 + digit;
1648 const auto last_digit =
static_cast<Value::UInt
>(value % 10);
1649 decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
1650 }
else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
1651 decoded = Value::LargestInt(value);
1659bool OurReader::decodeDouble(Token& token) {
1661 if (!decodeDouble(token, decoded))
1663 currentValue().swapPayload(decoded);
1664 currentValue().setOffsetStart(token.start_ - begin_);
1665 currentValue().setOffsetLimit(token.end_ - begin_);
1669bool OurReader::decodeDouble(Token& token, Value& decoded) {
1672 is.imbue(std::locale::classic());
1673 if (!(is >> value)) {
1674 if (value == std::numeric_limits<double>::max())
1675 value = std::numeric_limits<double>::infinity();
1676 else if (value == std::numeric_limits<double>::lowest())
1677 value = -std::numeric_limits<double>::infinity();
1684 else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
1686 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
1692bool OurReader::decodeString(Token& token) {
1694 if (!decodeString(token, decoded_string))
1696 Value decoded(decoded_string);
1697 currentValue().swapPayload(decoded);
1698 currentValue().setOffsetStart(token.start_ - begin_);
1699 currentValue().setOffsetLimit(token.end_ - begin_);
1703bool OurReader::decodeString(Token& token, String& decoded) {
1704 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
1705 Location current = token.start_ + 1;
1706 Location end = token.end_ - 1;
1707 while (current != end) {
1708 Char c = *current++;
1713 return addError(
"Empty escape sequence in string", token, current);
1714 Char escape = *current++;
1741 unsigned int unicode;
1742 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1744 decoded += codePointToUTF8(unicode);
1747 return addError(
"Bad escape sequence in string", token, current);
1750 if (
static_cast<unsigned char>(c) < 0x20)
1751 return addError(
"Control character in string", token, current - 1);
1758bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1759 Location end,
unsigned int& unicode) {
1761 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1763 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1765 if (end - current < 6)
1767 "additional six characters expected to parse unicode surrogate pair.",
1769 if (*(current++) ==
'\\' && *(current++) ==
'u') {
1770 unsigned int surrogatePair;
1771 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1772 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1776 return addError(
"expecting another \\u token to begin the second half of "
1777 "a unicode surrogate pair",
1783bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1785 unsigned int& ret_unicode) {
1786 if (end - current < 4)
1788 "Bad unicode escape sequence in string: four digits expected.", token,
1791 for (
int index = 0; index < 4; ++index) {
1792 Char c = *current++;
1794 if (c >=
'0' && c <=
'9')
1796 else if (c >=
'a' && c <=
'f')
1797 unicode += c -
'a' + 10;
1798 else if (c >=
'A' && c <=
'F')
1799 unicode += c -
'A' + 10;
1802 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1805 ret_unicode =
static_cast<unsigned int>(unicode);
1809bool OurReader::addError(
const String& message, Token& token, Location extra) {
1811 info.token_ = token;
1812 info.message_ = message;
1813 info.extra_ = extra;
1814 errors_.push_back(info);
1818bool OurReader::recoverFromError(TokenType skipUntilToken) {
1819 size_t errorCount = errors_.size();
1822 if (!readToken(skip))
1823 errors_.resize(errorCount);
1824 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1827 errors_.resize(errorCount);
1831bool OurReader::addErrorAndRecover(
const String& message, Token& token,
1832 TokenType skipUntilToken) {
1833 addError(message, token);
1834 return recoverFromError(skipUntilToken);
1837Value& OurReader::currentValue() {
return *(nodes_.top()); }
1839OurReader::Char OurReader::getNextChar() {
1840 if (current_ == end_)
1845void OurReader::getLocationLineAndColumn(Location location,
int& line,
1846 int& column)
const {
1847 Location current = begin_;
1848 Location lastLineStart = current;
1850 while (current < location && current != end_) {
1851 Char c = *current++;
1853 if (current != end_ && *current ==
'\n')
1855 lastLineStart = current;
1857 }
else if (c ==
'\n') {
1858 lastLineStart = current;
1863 column = int(location - lastLineStart) + 1;
1867String OurReader::getLocationLineAndColumn(Location location)
const {
1869 getLocationLineAndColumn(location, line, column);
1870 char buffer[18 + 16 + 16 + 1];
1871 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
1875String OurReader::getFormattedErrorMessages()
const {
1877 for (
const auto& error : errors_) {
1879 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
1880 formattedMessage +=
" " + error.message_ +
"\n";
1883 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
1885 return formattedMessage;
1888std::vector<CharReader::StructuredError>
1889OurReader::getStructuredErrors()
const {
1890 std::vector<CharReader::StructuredError> allErrors;
1891 for (
const auto& error : errors_) {
1892 CharReader::StructuredError structured;
1893 structured.offset_start = error.token_.start_ - begin_;
1894 structured.offset_limit = error.token_.end_ - begin_;
1895 structured.message = error.message_;
1896 allErrors.push_back(structured);
1901class OurCharReader :
public CharReader {
1904 OurCharReader(
bool collectComments, OurFeatures
const& features)
1906 std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1909 class OurImpl :
public Impl {
1911 OurImpl(
bool collectComments, OurFeatures
const& features)
1912 : collectComments_(collectComments), reader_(features) {}
1914 bool parse(
char const* beginDoc,
char const* endDoc, Value* root,
1915 String* errs)
override {
1916 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1918 *errs = reader_.getFormattedErrorMessages();
1923 std::vector<CharReader::StructuredError>
1924 getStructuredErrors()
const override {
1925 return reader_.getStructuredErrors();
1929 bool const collectComments_;
1937 bool collectComments =
settings_[
"collectComments"].asBool();
1938 OurFeatures features = OurFeatures::all();
1939 features.allowComments_ =
settings_[
"allowComments"].asBool();
1940 features.allowTrailingCommas_ =
settings_[
"allowTrailingCommas"].asBool();
1941 features.strictRoot_ =
settings_[
"strictRoot"].asBool();
1942 features.allowDroppedNullPlaceholders_ =
1943 settings_[
"allowDroppedNullPlaceholders"].asBool();
1944 features.allowNumericKeys_ =
settings_[
"allowNumericKeys"].asBool();
1945 features.allowSingleQuotes_ =
settings_[
"allowSingleQuotes"].asBool();
1949 features.stackLimit_ =
static_cast<size_t>(
settings_[
"stackLimit"].asUInt());
1950 features.failIfExtra_ =
settings_[
"failIfExtra"].asBool();
1951 features.rejectDupKeys_ =
settings_[
"rejectDupKeys"].asBool();
1952 features.allowSpecialFloats_ =
settings_[
"allowSpecialFloats"].asBool();
1953 features.skipBom_ =
settings_[
"skipBom"].asBool();
1954 return new OurCharReader(collectComments, features);
1958 static const auto& valid_keys = *
new std::set<String>{
1961 "allowTrailingCommas",
1963 "allowDroppedNullPlaceholders",
1965 "allowSingleQuotes",
1969 "allowSpecialFloats",
1973 auto key = si.name();
1974 if (valid_keys.count(key))
1977 (*invalid)[key] = *si;
1981 return invalid ? invalid->
empty() :
true;
1990 (*settings)[
"allowComments"] =
false;
1991 (*settings)[
"allowTrailingCommas"] =
false;
1992 (*settings)[
"strictRoot"] =
true;
1993 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1994 (*settings)[
"allowNumericKeys"] =
false;
1995 (*settings)[
"allowSingleQuotes"] =
false;
1996 (*settings)[
"stackLimit"] = 256;
1997 (*settings)[
"failIfExtra"] =
true;
1998 (*settings)[
"rejectDupKeys"] =
true;
1999 (*settings)[
"allowSpecialFloats"] =
false;
2000 (*settings)[
"skipBom"] =
true;
2006 (*settings)[
"collectComments"] =
true;
2007 (*settings)[
"allowComments"] =
true;
2008 (*settings)[
"allowTrailingCommas"] =
true;
2009 (*settings)[
"strictRoot"] =
false;
2010 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
2011 (*settings)[
"allowNumericKeys"] =
false;
2012 (*settings)[
"allowSingleQuotes"] =
false;
2013 (*settings)[
"stackLimit"] = 256;
2014 (*settings)[
"failIfExtra"] =
false;
2015 (*settings)[
"rejectDupKeys"] =
false;
2016 (*settings)[
"allowSpecialFloats"] =
false;
2017 (*settings)[
"skipBom"] =
true;
2023 (*settings)[
"allowComments"] =
false;
2024 (*settings)[
"allowTrailingCommas"] =
false;
2025 (*settings)[
"strictRoot"] =
false;
2026 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
2027 (*settings)[
"allowNumericKeys"] =
false;
2028 (*settings)[
"allowSingleQuotes"] =
false;
2029 (*settings)[
"stackLimit"] = 256;
2030 (*settings)[
"failIfExtra"] =
true;
2031 (*settings)[
"rejectDupKeys"] =
false;
2032 (*settings)[
"allowSpecialFloats"] =
false;
2033 (*settings)[
"skipBom"] =
false;
2037std::vector<CharReader::StructuredError>
2039 return _impl->getStructuredErrors();
2044 return _impl->parse(beginDoc, endDoc, root, errs);
2053 ssin << sin.rdbuf();
2054 String doc = std::move(ssin).str();
2055 char const* begin = doc.data();
2056 char const* end = begin + doc.size();
2059 return reader->parse(begin, end, root, errs);
virtual CharReader * newCharReader() const =0
Allocate a CharReader via operator new().
Build a CharReader implementation.
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
static void ecma404Mode(Json::Value *settings)
ECMA-404 mode.
Value & operator[](const String &key)
A simple way to update a specific setting.
CharReader * newCharReader() const override
Allocate a CharReader via operator new().
static void strictMode(Json::Value *settings)
Same as old Features::strictMode().
Json::Value settings_
Configuration of this builder.
~CharReaderBuilder() override
bool validate(Json::Value *invalid) const
Interface for reading JSON from a char array.
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
virtual bool parse(char const *beginDoc, char const *endDoc, Value *root, String *errs)
Read a Value from a JSON document.
Configuration passed to reader and writer.
bool strictRoot_
true if root must be either an array or an object value.
bool allowComments_
true if comments are allowed. Default: true.
bool allowDroppedNullPlaceholders_
true if dropped null placeholders are allowed. Default: false.
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
Features()
Initialize the configuration like JsonConfig::allFeatures;.
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
bool allowNumericKeys_
true if numeric object key are allowed. Default: false.
Reader()
Constructs a Reader allowing all features for parsing.
bool pushError(const Value &value, const String &message)
Add a semantic error message.
bool good() const
Return whether there are any errors.
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
String getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
String getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
void setComment(const char *comment, CommentPlacement placement)
ptrdiff_t getOffsetLimit() const
void swapPayload(Value &other)
Swap values but leave comments and source offsets in place.
void setOffsetLimit(ptrdiff_t limit)
Json::LargestInt LargestInt
Json::LargestUInt LargestUInt
void setOffsetStart(ptrdiff_t start)
static constexpr Int maxInt
Maximum signed int value that can be stored in a Json::Value.
static constexpr LargestUInt maxLargestUInt
Maximum unsigned integer value that can be stored in a Json::Value.
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
ptrdiff_t getOffsetStart() const
#define jsoncpp_snprintf
If defined, indicates that the source file is amalgamated to prevent private header inclusion.
#define JSONCPP_DEPRECATED_STACK_LIMIT
static size_t const stackLimit_g
JSON (JavaScript Object Notation).
size_t & newlineScanByteCountForTesting()
@ commentAfterOnSameLine
a comment just after a value on the same line
@ commentBefore
a comment placed on the line before a value
@ commentAfter
a comment on the line after a value (only make sense for
std::basic_istringstream< String::value_type, String::traits_type, String::allocator_type > IStringStream
std::unique_ptr< CharReader > CharReaderPtr
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
void throwRuntimeError(String const &msg)
used internally
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
IStream & operator>>(IStream &, Value &)
Read from 'sin' into 'root'.
bool parseFromStream(CharReader::Factory const &, IStream &, Value *root, String *errs)
Consume entire stream and use its begin/end.
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
An error tagged with where in the JSON text it was encountered.