Skip to content

Expose json::value::parse for UTF8 string on Windows #1350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions Release/include/cpprest/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,17 +390,37 @@ class value
/// <summary>
/// Parses a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the
/// platform-native character width</param>
_ASYNCRTIMP static value __cdecl parse(const utility::string_t& value);

/// <summary>
/// Attempts to parse a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL double-byte string</param>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string of the
/// platform-native character width</param>
/// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
_ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode);

#ifdef _WIN32
/// <summary>
/// Parses a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string in
/// UTF8 format</param>
_ASYNCRTIMP static value __cdecl parse(const std::string& value);

/// <summary>
/// Attempts to parse a string and construct a JSON value.
/// </summary>
/// <param name="value">The C++ value to create a JSON value from, a C++ STL string in
/// UTF8 format</param>
/// <param name="errorCode">If parsing fails, the error code is greater than 0</param>
/// <returns>The parsed object. Returns web::json::value::null if failed</returns>
_ASYNCRTIMP static value __cdecl parse(const std::string& value, std::error_code& errorCode);
#endif

/// <summary>
/// Serializes the current JSON value to a C++ string.
/// </summary>
Expand Down
93 changes: 31 additions & 62 deletions Release/src/json/json_parsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1120,10 +1120,11 @@ std::unique_ptr<web::json::details::_Value> JSON_Parser<CharType>::_ParseValue(
} // namespace json
} // namespace web

static web::json::value _parse_stream(utility::istream_t& stream)
template<typename CharType>
static web::json::value _parse_stream(std::basic_istream<CharType>& stream)
{
web::json::details::JSON_StreamParser<utility::char_t> parser(stream);
web::json::details::JSON_Parser<utility::char_t>::Token tkn;
web::json::details::JSON_StreamParser<CharType> parser(stream);
typename web::json::details::JSON_Parser<CharType>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
Expand All @@ -1136,18 +1137,19 @@ static web::json::value _parse_stream(utility::istream_t& stream)
{
web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message()));
}
else if (tkn.kind != web::json::details::JSON_Parser<utility::char_t>::Token::TKN_EOF)
else if (tkn.kind != web::json::details::JSON_Parser<CharType>::Token::TKN_EOF)
{
web::json::details::CreateException(tkn,
_XPLATSTR("Left-over characters in stream after parsing a JSON value"));
}
return value;
}

static web::json::value _parse_stream(utility::istream_t& stream, std::error_code& error)
template<typename CharType>
static web::json::value _parse_stream(std::basic_istream<CharType>& stream, std::error_code& error)
{
web::json::details::JSON_StreamParser<utility::char_t> parser(stream);
web::json::details::JSON_Parser<utility::char_t>::Token tkn;
web::json::details::JSON_StreamParser<CharType> parser(stream);
typename web::json::details::JSON_Parser<CharType>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
Expand All @@ -1157,7 +1159,7 @@ static web::json::value _parse_stream(utility::istream_t& stream, std::error_cod
}

auto returnObject = parser.ParseValue(tkn);
if (tkn.kind != web::json::details::JSON_Parser<utility::char_t>::Token::TKN_EOF)
if (tkn.kind != web::json::details::JSON_Parser<CharType>::Token::TKN_EOF)
{
web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream);
}
Expand All @@ -1166,11 +1168,11 @@ static web::json::value _parse_stream(utility::istream_t& stream, std::error_cod
return returnObject;
}

#ifdef _WIN32
static web::json::value _parse_narrow_stream(std::istream& stream)
template<typename CharType>
static web::json::value _parse_string(const std::basic_string<CharType>& str)
{
web::json::details::JSON_StreamParser<char> parser(stream);
web::json::details::JSON_StreamParser<char>::Token tkn;
web::json::details::JSON_StringParser<CharType> parser(str);
typename web::json::details::JSON_Parser<CharType>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
Expand All @@ -1183,18 +1185,19 @@ static web::json::value _parse_narrow_stream(std::istream& stream)
{
web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message()));
}
else if (tkn.kind != web::json::details::JSON_Parser<char>::Token::TKN_EOF)
else if (tkn.kind != web::json::details::JSON_Parser<CharType>::Token::TKN_EOF)
{
web::json::details::CreateException(tkn,
_XPLATSTR("Left-over characters in stream after parsing a JSON value"));
}
return value;
}

static web::json::value _parse_narrow_stream(std::istream& stream, std::error_code& error)
template<typename CharType>
static web::json::value _parse_string(const std::basic_string<CharType>& str, std::error_code& error)
{
web::json::details::JSON_StreamParser<char> parser(stream);
web::json::details::JSON_StreamParser<char>::Token tkn;
web::json::details::JSON_StringParser<CharType> parser(str);
typename web::json::details::JSON_Parser<CharType>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
Expand All @@ -1204,7 +1207,7 @@ static web::json::value _parse_narrow_stream(std::istream& stream, std::error_co
}

auto returnObject = parser.ParseValue(tkn);
if (tkn.kind != web::json::details::JSON_Parser<utility::char_t>::Token::TKN_EOF)
if (tkn.kind != web::json::details::JSON_Parser<CharType>::Token::TKN_EOF)
{
returnObject = web::json::value();
web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream);
Expand All @@ -1213,53 +1216,12 @@ static web::json::value _parse_narrow_stream(std::istream& stream, std::error_co
error = std::move(tkn.m_error);
return returnObject;
}
#endif

web::json::value web::json::value::parse(const utility::string_t& str)
{
web::json::details::JSON_StringParser<utility::char_t> parser(str);
web::json::details::JSON_Parser<utility::char_t>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
{
web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message()));
}

auto value = parser.ParseValue(tkn);
if (tkn.m_error)
{
web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message()));
}
else if (tkn.kind != web::json::details::JSON_Parser<utility::char_t>::Token::TKN_EOF)
{
web::json::details::CreateException(tkn,
_XPLATSTR("Left-over characters in stream after parsing a JSON value"));
}
return value;
}
web::json::value web::json::value::parse(const utility::string_t& str) { return _parse_string(str); }

web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error)
{
web::json::details::JSON_StringParser<utility::char_t> parser(str);
web::json::details::JSON_Parser<utility::char_t>::Token tkn;

parser.GetNextToken(tkn);
if (tkn.m_error)
{
error = std::move(tkn.m_error);
return web::json::value();
}

auto returnObject = parser.ParseValue(tkn);
if (tkn.kind != web::json::details::JSON_Parser<utility::char_t>::Token::TKN_EOF)
{
returnObject = web::json::value();
web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream);
}

error = std::move(tkn.m_error);
return returnObject;
return _parse_string(str, error);
}

web::json::value web::json::value::parse(utility::istream_t& stream) { return _parse_stream(stream); }
Expand All @@ -1270,10 +1232,17 @@ web::json::value web::json::value::parse(utility::istream_t& stream, std::error_
}

#ifdef _WIN32
web::json::value web::json::value::parse(std::istream& stream) { return _parse_narrow_stream(stream); }
web::json::value web::json::value::parse(const std::string& str) { return _parse_string(str); }

web::json::value web::json::value::parse(const std::string& str, std::error_code& error)
{
return _parse_string(str, error);
}

web::json::value web::json::value::parse(std::istream& stream) { return _parse_stream(stream); }

web::json::value web::json::value::parse(std::istream& stream, std::error_code& error)
{
return _parse_narrow_stream(stream, error);
return _parse_stream(stream, error);
}
#endif
26 changes: 16 additions & 10 deletions Release/tests/functional/json/parsing_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ SUITE(parsing_tests)
VERIFY_ARE_EQUAL(U("K"), str.as_string());

str = json::value::parse(U("\"\\u20AC\""));
// Euro sign as a hexidecmial UTF-8
// Euro sign as a hexadecimal UTF-8
const auto euro = to_string_t("\xE2\x82\xAC");
VERIFY_ARE_EQUAL(euro, str.as_string());

Expand Down Expand Up @@ -631,47 +631,53 @@ SUITE(parsing_tests)
TEST(byte_ptr_parsing_array)
{
char s[] = "[ \"test1\",true]";
json::value v = json::value::parse(s);
std::stringstream ss;
ss << s;
json::value v = json::value::parse(ss);
auto s2 = v.serialize();
json::value vv = json::value::parse(ss);
VERIFY_ARE_EQUAL(v, vv);

auto s2 = v.serialize();
VERIFY_ARE_EQUAL(s2, U("[\"test1\",true]"));

std::stringstream os;
v.serialize(os);
vv.serialize(os);
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
}

TEST(byte_ptr_parsing_object)
{
char s[] = "{\"test1\":true }";
json::value v = json::value::parse(s);
std::stringstream ss;
ss << s;
json::value v = json::value::parse(ss);
auto s2 = v.serialize();
json::value vv = json::value::parse(ss);
VERIFY_ARE_EQUAL(v, vv);

auto s2 = v.serialize();
VERIFY_ARE_EQUAL(s2, U("{\"test1\":true}"));

std::stringstream os;
v.serialize(os);
vv.serialize(os);
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
}

TEST(Japanese)
{
utility::string_t ws = U("\"こんにちは\"");
std::string s = to_utf8string(ws);
json::value v = json::value::parse(s);

std::stringstream ss;
ss << s;
json::value v = json::value::parse(ss);
auto s2 = v.serialize();
json::value vv = json::value::parse(ss);
VERIFY_ARE_EQUAL(v, vv);

auto s2 = v.serialize();
VERIFY_ARE_EQUAL(s2, ws);

std::stringstream os;
v.serialize(os);
vv.serialize(os);
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
}

Expand Down