Stricter JSON standard conformance (#2153)

This commit is contained in:
Conor Burgess 2018-02-13 13:15:53 +00:00 committed by Aleksandar Fabijanic
parent bc39165811
commit af09a02a34
7 changed files with 60 additions and 12 deletions

View File

@ -57,13 +57,17 @@ struct Foundation_API UTF8
/// Remove the UTF-8 Byte Order Mark sequence (0xEF, 0xBB, 0xBF)
/// from the beginning of the string, if it's there.
static std::string escape(const std::string& s);
static std::string escape(const std::string& s, bool strictJSON = false);
/// Escapes a string. Special characters like tab, backslash, ... are
/// escaped. Unicode characters are escaped to \uxxxx.
/// If strictJSON is true, \a and \v will be escaped to \\u0007 and \\u000B
/// instead of \\a and \\v for strict JSON conformance.
static std::string escape(const std::string::const_iterator& begin, const std::string::const_iterator& end);
static std::string escape(const std::string::const_iterator& begin, const std::string::const_iterator& end, bool strictJSON = false);
/// Escapes a string. Special characters like tab, backslash, ... are
/// escaped. Unicode characters are escaped to \uxxxx.
/// If strictJSON is true, \a and \v will be escaped to \\u0007 and \\u000B
/// instead of \\a and \\v for strict JSON conformance.
static std::string unescape(const std::string& s);
/// Creates an UTF8 string from a string that contains escaped characters.

View File

@ -42,7 +42,7 @@ void writeString(const std::string &value, T& obj, typename WriteFunc<T, S>::Typ
if(wrap) (obj.*write)("\"", 1);
if(escapeAllUnicode)
{
std::string str = Poco::UTF8::escape(value.begin(), value.end());
std::string str = Poco::UTF8::escape(value.begin(), value.end(), true);
(obj.*write)(str.c_str(), str.size());
}
else
@ -52,7 +52,7 @@ void writeString(const std::string &value, T& obj, typename WriteFunc<T, S>::Typ
// Forward slash isn't strictly required by JSON spec, but some parsers expect it
if((*it >= 0 && *it <= 31) || (*it == '"') || (*it == '\\') || (*it == '/'))
{
std::string str = Poco::UTF8::escape(it, it + 1);
std::string str = Poco::UTF8::escape(it, it + 1, true);
(obj.*write)(str.c_str(), str.size());
}else (obj.*write)(&(*it), 1);
}

View File

@ -171,12 +171,12 @@ void UTF8::removeBOM(std::string& str)
}
}
std::string UTF8::escape(const std::string &s)
std::string UTF8::escape(const std::string &s, bool strictJSON)
{
return escape(s.begin(), s.end());
return escape(s.begin(), s.end(), strictJSON);
}
std::string UTF8::escape(const std::string::const_iterator& begin, const std::string::const_iterator& end)
std::string UTF8::escape(const std::string::const_iterator& begin, const std::string::const_iterator& end, bool strictJSON)
{
static Poco::UInt32 offsetsFromUTF8[6] = {
0x00000000UL, 0x00003080UL, 0x000E2080UL,
@ -206,8 +206,8 @@ std::string UTF8::escape(const std::string::const_iterator& begin, const std::st
else if (ch == '\r') result += "\\r";
else if (ch == '\b') result += "\\b";
else if (ch == '\f') result += "\\f";
else if (ch == '\v') result += "\\v";
else if (ch == '\a') result += "\\a";
else if (ch == '\v') result += (strictJSON ? "\\u000B" : "\\v");
else if (ch == '\a') result += (strictJSON ? "\\u0007" : "\\a");
else if (ch == '\\') result += "\\\\";
else if (ch == '\"') result += "\\\"";
else if (ch == '/') result += "\\/";

View File

@ -1195,13 +1195,13 @@ void StringTest::testJSONString()
assert (toJSON("\\", false) == "\\\\");
assert (toJSON("\"", false) == "\\\"");
assert (toJSON("/", false) == "\\/");
assert (toJSON("\a", false) == "\\a");
assert (toJSON("\a", false) == "\\u0007");
assert (toJSON("\b", false) == "\\b");
assert (toJSON("\f", false) == "\\f");
assert (toJSON("\n", false) == "\\n");
assert (toJSON("\r", false) == "\\r");
assert (toJSON("\t", false) == "\\t");
assert (toJSON("\v", false) == "\\v");
assert (toJSON("\v", false) == "\\u000B");
assert (toJSON("a", false) == "a");
assert (toJSON("\xD0\x82", 0) == "\xD0\x82");
assert (toJSON("\xD0\x82", Poco::JSON_ESCAPE_UNICODE) == "\\u0402");

View File

@ -83,6 +83,25 @@ void UTF8StringTest::testTransform()
}
void UTF8StringTest::testEscape()
{
std::string s1("A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82");
assert (UTF8::escape(s1) == "A \\t, a \\v, and an \\a walk into a |, and the barman says \\u0402");
assert (UTF8::escape(s1, true) == "A \\t, a \\u000B, and an \\u0007 walk into a |, and the barman says \\u0402");
}
void UTF8StringTest::testUnescape()
{
std::string s1("A \\t, a \\u000B, and an \\u0007 walk into a |, and the barman says \\u0402");
std::string s2("A \\t, a \\v, and an \\a walk into a |, and the barman says \\u0402");
assert (UTF8::unescape(s1) == "A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82");
assert (UTF8::unescape(s2) == "A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82");
}
void UTF8StringTest::setUp()
{
}
@ -99,6 +118,8 @@ CppUnit::Test* UTF8StringTest::suite()
CppUnit_addTest(pSuite, UTF8StringTest, testCompare);
CppUnit_addTest(pSuite, UTF8StringTest, testTransform);
CppUnit_addTest(pSuite, UTF8StringTest, testEscape);
CppUnit_addTest(pSuite, UTF8StringTest, testUnescape);
return pSuite;
}

View File

@ -27,6 +27,9 @@ public:
void testCompare();
void testTransform();
void testEscape();
void testUnescape();
void setUp();
void tearDown();

View File

@ -1930,7 +1930,6 @@ void JSONTest::testEscape0()
void JSONTest::testNonEscapeUnicode()
{
Poco::JSON::Object::Ptr json = new Poco::JSON::Object();
std::string chinese("{ \"name\" : \"\\u4e2d\" }");
Poco::JSON::Parser parser(new Poco::JSON::ParseHandler());
Var result = parser.parse(chinese);
@ -1951,6 +1950,27 @@ void JSONTest::testNonEscapeUnicode()
object = result.extract<Object::Ptr>();
ss.str(""); object->stringify(ss);
assert (ss.str() == "{\"name\":\"g\xC3\xBCnter\"}");
Poco::JSON::Object obj1;
std::string shortEscapeStr("String with \t");
std::string longEscapeStr("String with \a and \v plus \t for good measure");
obj1.set("shortEscape", shortEscapeStr);
obj1.set("longEscape", longEscapeStr);
ss.str("");
obj1.stringify(ss);
parser.reset();
parser.parse(ss.str());
result = parser.asVar();
assert(result.type() == typeid(Object::Ptr));
object = result.extract<Object::Ptr>();
Var shortEscape = object->get("shortEscape");
Var longEscape = object->get("longEscape");
assert(shortEscape.convert<std::string>() == shortEscapeStr);
assert(longEscape.convert<std::string>() == longEscapeStr);
}