Skip to content

Commit 343e6a3

Browse files
author
Blake Gross
committed
Added non-throwing json parse by overloading web::json::value::parse to accept a std::error_code which becomes greater than 0 when an error occurs
1 parent 470a9f7 commit 343e6a3

File tree

2 files changed

+146
-36
lines changed

2 files changed

+146
-36
lines changed

Release/include/cpprest/json.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ namespace json
391391
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
392392
_ASYNCRTIMP static value parse(const utility::string_t&);
393393

394+
/// <summary>
395+
/// Attempts to parse a string and construct a JSON value.
396+
/// </summary>
397+
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
398+
/// <param name="errorCode">If parsing fails, the error code is filled with a protocol error</param>
399+
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
400+
_ASYNCRTIMP static value parse(const utility::string_t&, std::error_code&);
401+
394402
/// <summary>
395403
/// Serializes the current JSON value to a C++ string.
396404
/// </summary>

Release/src/json/json_parsing.cpp

Lines changed: 138 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ void CreateError(const Token &tk, const utility::string_t &message)
7070
throw web::json::json_exception(os.str().c_str());
7171
}
7272

73+
struct JsonParseStatus
74+
{
75+
bool didSucceed;
76+
string_t errorMsg;
77+
78+
JsonParseStatus() :
79+
didSucceed(true),
80+
errorMsg(L"")
81+
{
82+
}
83+
};
84+
7385
template <typename CharType>
7486
class JSON_Parser
7587
{
@@ -126,7 +138,7 @@ class JSON_Parser
126138
bool signed_number;
127139
};
128140

129-
void GetNextToken(Token &);
141+
JsonParseStatus GetNextToken(Token &);
130142

131143
web::json::value ParseValue(typename JSON_Parser<CharType>::Token &first)
132144
{
@@ -888,14 +900,16 @@ bool JSON_StringParser<CharType>::finish_parsing_string_with_unescape_char(typen
888900
}
889901

890902
template <typename CharType>
891-
void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token& result)
903+
JsonParseStatus JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token& result)
892904
{
905+
JsonParseStatus parseSucceed;
906+
893907
try_again:
894908
CharType ch = EatWhitespace();
895909

896910
CreateToken(result, Token::TKN_EOF);
897911

898-
if (ch == this->m_eof) return;
912+
if (ch == this->m_eof) return parseSucceed;
899913

900914
switch (ch)
901915
{
@@ -904,7 +918,8 @@ void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token&
904918
{
905919
if(++m_currentParsingDepth > JSON_Parser<CharType>::maxParsingDepth)
906920
{
907-
CreateError(result, _XPLATSTR("Nesting too deep!"));
921+
parseSucceed.didSucceed = false;
922+
parseSucceed.errorMsg = _XPLATSTR("Nesting too deep!");
908923
break;
909924
}
910925

@@ -917,7 +932,8 @@ void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token&
917932
{
918933
if((signed int)(--m_currentParsingDepth) < 0)
919934
{
920-
CreateError(result, _XPLATSTR("Mismatched braces!"));
935+
parseSucceed.didSucceed = false;
936+
parseSucceed.errorMsg = _XPLATSTR("Mismatched braces!");
921937
break;
922938
}
923939

@@ -934,20 +950,41 @@ void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token&
934950
break;
935951

936952
case 't':
937-
if (!CompleteKeywordTrue(result)) CreateError(result, _XPLATSTR("Malformed literal"));
953+
if (!CompleteKeywordTrue(result))
954+
{
955+
parseSucceed.didSucceed = false;
956+
parseSucceed.errorMsg = _XPLATSTR("Malformed literal");
957+
}
938958
break;
939959
case 'f':
940-
if (!CompleteKeywordFalse(result)) CreateError(result, _XPLATSTR("Malformed literal"));
960+
if (!CompleteKeywordFalse(result))
961+
{
962+
parseSucceed.didSucceed = false;
963+
parseSucceed.errorMsg = _XPLATSTR("Malformed literal");
964+
}
941965
break;
942966
case 'n':
943-
if (!CompleteKeywordNull(result)) CreateError(result, _XPLATSTR("Malformed literal"));
967+
if (!CompleteKeywordNull(result))
968+
{
969+
parseSucceed.didSucceed = false;
970+
parseSucceed.errorMsg = _XPLATSTR("Malformed literal");
971+
}
944972
break;
945973
case '/':
946-
if ( !CompleteComment(result) ) CreateError(result, _XPLATSTR("Malformed comment"));
974+
if (!CompleteComment(result))
975+
{
976+
parseSucceed.didSucceed = false;
977+
parseSucceed.errorMsg = _XPLATSTR("Malformed comment");
978+
break;
979+
}
947980
// For now, we're ignoring comments.
948981
goto try_again;
949982
case '"':
950-
if ( !CompleteStringLiteral(result) ) CreateError(result, _XPLATSTR("Malformed string literal"));
983+
if (!CompleteStringLiteral(result))
984+
{
985+
parseSucceed.didSucceed = false;
986+
parseSucceed.errorMsg = _XPLATSTR("Malformed string literal");
987+
}
951988
break;
952989

953990
case '-':
@@ -961,30 +998,41 @@ void JSON_Parser<CharType>::GetNextToken(typename JSON_Parser<CharType>::Token&
961998
case '7':
962999
case '8':
9631000
case '9':
964-
if ( !CompleteNumberLiteral(ch, result) ) CreateError(result, _XPLATSTR("Malformed numeric literal"));
1001+
if (!CompleteNumberLiteral(ch, result))
1002+
{
1003+
parseSucceed.didSucceed = false;
1004+
parseSucceed.errorMsg = _XPLATSTR("Malformed numeric literal");
1005+
}
9651006
break;
9661007
default:
967-
CreateError(result, _XPLATSTR("Malformed token"));
1008+
parseSucceed.didSucceed = false;
1009+
parseSucceed.errorMsg = _XPLATSTR("Malformed token");
9681010
break;
9691011
}
1012+
1013+
return parseSucceed;
9701014
}
9711015

9721016
template <typename CharType>
9731017
std::unique_ptr<web::json::details::_Object> JSON_Parser<CharType>::_ParseObject(typename JSON_Parser<CharType>::Token &tkn)
9741018
{
975-
GetNextToken(tkn);
1019+
JsonParseStatus parseAttempt = GetNextToken(tkn);
1020+
if (!parseAttempt.didSucceed)
1021+
{
1022+
CreateError(tkn, parseAttempt.errorMsg);
1023+
}
9761024

9771025
auto obj = utility::details::make_unique<web::json::details::_Object>(g_keep_json_object_unsorted);
9781026
auto& elems = obj->m_object.m_elements;
9791027

980-
if ( tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBrace )
1028+
if ( tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBrace )
9811029
{
9821030
while ( true )
9831031
{
9841032
// State 1: New field or end of object, looking for field name or closing brace
9851033

9861034
std::basic_string<CharType> fieldName;
987-
1035+
9881036
switch ( tkn.kind )
9891037
{
9901038
case JSON_Parser<CharType>::Token::TKN_StringLiteral:
@@ -994,14 +1042,15 @@ std::unique_ptr<web::json::details::_Object> JSON_Parser<CharType>::_ParseObject
9941042
goto error;
9951043
}
9961044

997-
GetNextToken(tkn);
1045+
parseAttempt = GetNextToken(tkn);
1046+
if (!parseAttempt.didSucceed) goto getNextTokenError;
9981047

9991048
// State 2: Looking for a colon.
10001049

10011050
if ( tkn.kind != JSON_Parser<CharType>::Token::TKN_Colon ) goto done;
10021051

1003-
GetNextToken(tkn);
1004-
1052+
parseAttempt = GetNextToken(tkn);
1053+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10051054
// State 3: Looking for an expression.
10061055
#ifdef ENABLE_JSON_VALUE_VISUALIZER
10071056
auto fieldValue = _ParseValue(tkn);
@@ -1017,7 +1066,8 @@ std::unique_ptr<web::json::details::_Object> JSON_Parser<CharType>::_ParseObject
10171066
switch (tkn.kind)
10181067
{
10191068
case JSON_Parser<CharType>::Token::TKN_Comma:
1020-
GetNextToken(tkn);
1069+
parseAttempt = GetNextToken(tkn);
1070+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10211071
break;
10221072
case JSON_Parser<CharType>::Token::TKN_CloseBrace:
10231073
goto done;
@@ -1030,6 +1080,7 @@ std::unique_ptr<web::json::details::_Object> JSON_Parser<CharType>::_ParseObject
10301080
done:
10311081

10321082
GetNextToken(tkn);
1083+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10331084

10341085
if (!g_keep_json_object_unsorted) {
10351086
::std::sort(elems.begin(), elems.end(), json::object::compare_pairs);
@@ -1040,16 +1091,23 @@ std::unique_ptr<web::json::details::_Object> JSON_Parser<CharType>::_ParseObject
10401091
error:
10411092

10421093
CreateError(tkn, _XPLATSTR("Malformed object literal"));
1094+
1095+
getNextTokenError:
1096+
CreateError(tkn, parseAttempt.errorMsg);
10431097
}
10441098

10451099
template <typename CharType>
10461100
std::unique_ptr<web::json::details::_Array> JSON_Parser<CharType>::_ParseArray(typename JSON_Parser<CharType>::Token &tkn)
10471101
{
1048-
GetNextToken(tkn);
1102+
JsonParseStatus parseAttempt = GetNextToken(tkn);
1103+
if (!parseAttempt.didSucceed)
1104+
{
1105+
CreateError(tkn, parseAttempt.errorMsg);
1106+
}
10491107

10501108
auto result = utility::details::make_unique<web::json::details::_Array>();
10511109

1052-
if ( tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBracket )
1110+
if ( tkn.kind != JSON_Parser<CharType>::Token::TKN_CloseBracket )
10531111
{
10541112
while ( true )
10551113
{
@@ -1060,10 +1118,12 @@ std::unique_ptr<web::json::details::_Array> JSON_Parser<CharType>::_ParseArray(t
10601118
switch (tkn.kind)
10611119
{
10621120
case JSON_Parser<CharType>::Token::TKN_Comma:
1063-
GetNextToken(tkn);
1121+
parseAttempt = GetNextToken(tkn);
1122+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10641123
break;
10651124
case JSON_Parser<CharType>::Token::TKN_CloseBracket:
1066-
GetNextToken(tkn);
1125+
parseAttempt = GetNextToken(tkn);
1126+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10671127

10681128
return result;
10691129
default:
@@ -1072,26 +1132,34 @@ std::unique_ptr<web::json::details::_Array> JSON_Parser<CharType>::_ParseArray(t
10721132
}
10731133
}
10741134

1075-
GetNextToken(tkn);
1135+
parseAttempt = GetNextToken(tkn);
1136+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10761137

10771138
return result;
1139+
1140+
getNextTokenError:
1141+
CreateError(tkn, parseAttempt.errorMsg);
10781142
}
10791143

10801144
template <typename CharType>
10811145
std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseValue(typename JSON_Parser<CharType>::Token &tkn)
10821146
{
1147+
JsonParseStatus parseAttempt;
10831148
switch (tkn.kind)
10841149
{
10851150
case JSON_Parser<CharType>::Token::TKN_OpenBrace:
1086-
return _ParseObject(tkn);
1087-
1151+
{
1152+
return _ParseObject(tkn);
1153+
}
10881154
case JSON_Parser<CharType>::Token::TKN_OpenBracket:
1089-
return _ParseArray(tkn);
1090-
1155+
{
1156+
return _ParseArray(tkn);
1157+
}
10911158
case JSON_Parser<CharType>::Token::TKN_StringLiteral:
10921159
{
10931160
auto value = utility::details::make_unique<web::json::details::_String>(std::move(tkn.string_val), tkn.has_unescape_symbol);
1094-
GetNextToken(tkn);
1161+
parseAttempt = GetNextToken(tkn);
1162+
if (!parseAttempt.didSucceed) goto getNextTokenError;
10951163
return std::move(value);
10961164
}
10971165

@@ -1103,31 +1171,40 @@ std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseValue(t
11031171
else
11041172
value = utility::details::make_unique<web::json::details::_Number>(tkn.uint64_val);
11051173

1106-
GetNextToken(tkn);
1174+
parseAttempt = GetNextToken(tkn);
1175+
if (!parseAttempt.didSucceed) goto getNextTokenError;
11071176
return std::move(value);
11081177
}
11091178

1110-
case JSON_Parser<CharType>::Token::TKN_NumberLiteral:
1179+
case JSON_Parser<CharType>::Token::TKN_NumberLiteral:
11111180
{
11121181
auto value = utility::details::make_unique<web::json::details::_Number>(tkn.double_val);
1113-
GetNextToken(tkn);
1182+
parseAttempt = GetNextToken(tkn);
1183+
if (!parseAttempt.didSucceed) goto getNextTokenError;
11141184
return std::move(value);
11151185
}
11161186
case JSON_Parser<CharType>::Token::TKN_BooleanLiteral:
11171187
{
11181188
auto value = utility::details::make_unique<web::json::details::_Boolean>(tkn.boolean_val);
1119-
GetNextToken(tkn);
1189+
parseAttempt = GetNextToken(tkn);
1190+
if (!parseAttempt.didSucceed) goto getNextTokenError;
11201191
return std::move(value);
11211192
}
11221193
case JSON_Parser<CharType>::Token::TKN_NullLiteral:
11231194
{
1124-
GetNextToken(tkn);
1195+
parseAttempt = GetNextToken(tkn);
1196+
if (!parseAttempt.didSucceed) goto getNextTokenError;
11251197
return utility::details::make_unique<web::json::details::_Null>();
11261198
}
11271199

11281200
default:
1129-
CreateError(tkn, _XPLATSTR("Unexpected token"));
1201+
{
1202+
return utility::details::make_unique<web::json::details::_Null>();
1203+
}
11301204
}
1205+
1206+
getNextTokenError:
1207+
CreateError(tkn, parseAttempt.errorMsg);
11311208
}
11321209

11331210
}}}
@@ -1170,7 +1247,12 @@ web::json::value web::json::value::parse(const utility::string_t& str)
11701247
web::json::details::JSON_StringParser<utility::char_t> parser(str);
11711248

11721249
web::json::details::JSON_Parser<utility::char_t>::Token tkn;
1173-
parser.GetNextToken(tkn);
1250+
auto parseAttempt = parser.GetNextToken(tkn);
1251+
1252+
if (!parseAttempt.didSucceed)
1253+
{
1254+
web::json::details::CreateError(tkn, parseAttempt.errorMsg);
1255+
}
11741256

11751257
auto value = parser.ParseValue(tkn);
11761258

@@ -1181,6 +1263,26 @@ web::json::value web::json::value::parse(const utility::string_t& str)
11811263
return value;
11821264
}
11831265

1266+
web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error)
1267+
{
1268+
web::json::details::JSON_StringParser<utility::char_t> parser(str);
1269+
1270+
web::json::details::JSON_Parser<utility::char_t>::Token tkn;
1271+
auto parseAttempt = parser.GetNextToken(tkn);
1272+
web::json::value returnObject;
1273+
1274+
if (parseAttempt.didSucceed)
1275+
{
1276+
returnObject = parser.ParseValue(tkn);
1277+
}
1278+
else
1279+
{
1280+
returnObject = web::json::value::null();
1281+
error = std::make_error_code(std::errc::protocol_error);
1282+
}
1283+
return returnObject;
1284+
}
1285+
11841286
web::json::value web::json::value::parse(utility::istream_t &stream)
11851287
{
11861288
return _parse_stream(stream);

0 commit comments

Comments
 (0)