fixed #592: Incorrect format string in Poco::Dynamic::Struct

This commit is contained in:
Guenter Obiltschnig 2014-11-07 10:43:50 +01:00
parent 6c37d53b39
commit fa4989e0c7
5 changed files with 151 additions and 35 deletions

View File

@ -542,6 +542,7 @@ private:
static Var parseObject(const std::string& val, std::string::size_type& pos);
static Var parseArray(const std::string& val, std::string::size_type& pos);
static std::string parseString(const std::string& val, std::string::size_type& pos);
static std::string parseJSONString(const std::string& val, std::string::size_type& pos);
static void skipWhiteSpace(const std::string& val, std::string::size_type& pos);
template <typename T>

View File

@ -401,6 +401,8 @@ Var Var::parse(const std::string& val, std::string::size_type& pos)
return parseObject(val, pos);
case '[':
return parseArray(val, pos);
case '"':
return parseJSONString(val, pos);
default:
return parseString(val, pos);
}
@ -464,41 +466,79 @@ Var Var::parseArray(const std::string& val, std::string::size_type& pos)
std::string Var::parseString(const std::string& val, std::string::size_type& pos)
{
static const std::string STR_STOP("\"");
static const std::string OTHER_STOP("\n ,]}");
bool inString = false;
//skip optional ' "
if (val[pos] == '"')
{
inString = true;
++pos;
}
std::string::size_type stop = std::string::npos;
if (inString)
{
stop = val.find_first_of(STR_STOP, pos);
if (stop == std::string::npos)
throw DataFormatException("Unterminated string");
return parseJSONString(val, pos);
}
else
{
// we stop at space, ',', ']' or '}' or end of string
stop = val.find_first_of(OTHER_STOP, pos);
if (stop == std::string::npos)
stop = val.size();
std::string::size_type safeCheck = val.find_first_of(STR_STOP, pos);
if (safeCheck != std::string::npos && safeCheck < stop)
throw DataFormatException("Misplaced string termination char found");
std::string result;
while (pos < val.size()
&& !Poco::Ascii::isSpace(val[pos])
&& val[pos] != ','
&& val[pos] != ']'
&& val[pos] != '}')
{
result += val[pos++];
}
return result;
}
}
// stop now points to the last char to be not included
std::string result = val.substr(pos, stop - pos);
++stop; // point past '/"
pos = stop;
std::string Var::parseJSONString(const std::string& val, std::string::size_type& pos)
{
poco_assert_dbg (val[pos] == '"');
++pos;
std::string result;
bool done = false;
while (pos < val.size() && !done)
{
switch (val[pos])
{
case '"':
done = true;
++pos;
break;
case '\\':
if (pos < val.size())
{
++pos;
switch (val[pos])
{
case 'b':
result += '\b';
break;
case 'f':
result += '\f';
break;
case 'n':
result += '\n';
break;
case 'r':
result += '\r';
break;
case 't':
result += '\t';
break;
default:
result += val[pos];
break;
}
break;
}
else
{
result += val[pos];
}
++pos;
break;
default:
result += val[pos++];
break;
}
}
if (!done) throw Poco::DataFormatException("unterminated JSON string");
return result;
}

View File

@ -35,6 +35,43 @@ VarHolder::~VarHolder()
namespace Impl {
void escape(std::string& target, const std::string& source)
{
std::string::const_iterator it(source.begin());
std::string::const_iterator end(source.end());
for (; it != end; ++it)
{
switch (*it)
{
case '"':
target += "\\\"";
break;
case '\\':
target += "\\\\";
break;
case '\b':
target += "\\b";
break;
case '\f':
target += "\\f";
break;
case '\n':
target += "\\n";
break;
case '\r':
target += "\\r";
break;
case '\t':
target += "\\t";
break;
default:
target += *it;
break;
}
}
}
bool isJSONString(const Var& any)
{
return any.type() == typeid(std::string) ||
@ -48,9 +85,9 @@ bool isJSONString(const Var& any)
void appendJSONString(std::string& val, const Var& any)
{
val.append(1, '"');
val.append(any.convert<std::string>());
val.append(1, '"');
val += '"';
escape(val, any.convert<std::string>());
val += '"';
}
@ -62,13 +99,21 @@ void appendJSONKey(std::string& val, const Var& any)
void appendJSONValue(std::string& val, const Var& any)
{
if (any.isEmpty()) val.append("null");
if (any.isEmpty())
{
val.append("null");
}
else
{
bool isStr = isJSONString(any);
if (isStr) val.append(1, '"');
val.append(any.convert<std::string>());
if (isStr) val.append(1, '"');
if (isStr)
{
appendJSONString(val, any.convert<std::string>());
}
else
{
val.append(any.convert<std::string>());
}
}
}

View File

@ -2089,6 +2089,20 @@ void VarTest::testArrayToString()
}
void VarTest::testArrayToStringEscape()
{
std::string s1("\"quoted string\"");
Poco::Int8 s2(23);
std::vector<Var> s16;
s16.push_back(s1);
s16.push_back(s2);
Var a1(s16);
std::string res = a1.convert<std::string>();
std::string expected("[ \"\\\"quoted string\\\"\", 23 ]");
assert (res == expected);
}
void VarTest::testStructToString()
{
DynamicStruct aStruct;
@ -2103,6 +2117,18 @@ void VarTest::testStructToString()
}
void VarTest::testStructToStringEscape()
{
DynamicStruct aStruct;
aStruct["Value"] = "Value with \" and \n";
Var a1(aStruct);
std::string res = a1.convert<std::string>();
std::string expected = "{ \"Value\" : \"Value with \\\" and \\n\" }";
assert (res == expected);
assert (aStruct.toString() == res);
}
void VarTest::testArrayOfStructsToString()
{
std::vector<Var> s16;
@ -2592,7 +2618,9 @@ CppUnit::Test* VarTest::suite()
CppUnit_addTest(pSuite, VarTest, testDynamicStructString);
CppUnit_addTest(pSuite, VarTest, testDynamicStructInt);
CppUnit_addTest(pSuite, VarTest, testArrayToString);
CppUnit_addTest(pSuite, VarTest, testArrayToStringEscape);
CppUnit_addTest(pSuite, VarTest, testStructToString);
CppUnit_addTest(pSuite, VarTest, testStructToStringEscape);
CppUnit_addTest(pSuite, VarTest, testArrayOfStructsToString);
CppUnit_addTest(pSuite, VarTest, testStructWithArraysToString);
CppUnit_addTest(pSuite, VarTest, testJSONDeserializeString);

View File

@ -59,7 +59,9 @@ public:
void testDynamicStructString();
void testDynamicStructInt();
void testArrayToString();
void testArrayToStringEscape();
void testStructToString();
void testStructToStringEscape();
void testArrayOfStructsToString();
void testStructWithArraysToString();
void testJSONDeserializeString();