mirror of
https://github.com/pocoproject/poco.git
synced 2025-04-27 10:25:59 +02:00
Merge pull request #53 from fbraem/develop
JSON unicode fixes and running tests on invalid unicode JSON
This commit is contained in:
commit
80cf789dcd
@ -38,6 +38,7 @@
|
|||||||
#include "Poco/JSON/JSONException.h"
|
#include "Poco/JSON/JSONException.h"
|
||||||
#include "Poco/Ascii.h"
|
#include "Poco/Ascii.h"
|
||||||
#include "Poco/Token.h"
|
#include "Poco/Token.h"
|
||||||
|
#include "Poco/UTF8Encoding.h"
|
||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@ -118,8 +119,8 @@ public:
|
|||||||
|
|
||||||
void finish(std::istream& istr)
|
void finish(std::istream& istr)
|
||||||
{
|
{
|
||||||
int c = istr.get();
|
int c = 0;
|
||||||
while (c != -1)
|
while ((c = istr.get()) != -1)
|
||||||
{
|
{
|
||||||
if ( c == 0 )
|
if ( c == 0 )
|
||||||
{
|
{
|
||||||
@ -134,6 +135,31 @@ public:
|
|||||||
if ( c == '"' )
|
if ( c == '"' )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if(0x80 <= c && c <= 0xFF)
|
||||||
|
{
|
||||||
|
int count = utf8_check_first(c);
|
||||||
|
if (!count)
|
||||||
|
{
|
||||||
|
throw JSONException(format("Unable to decode byte 0x%x", (unsigned int) c));
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[5];
|
||||||
|
buffer[0] = c;
|
||||||
|
for(int i = 1; i < count; ++i)
|
||||||
|
{
|
||||||
|
buffer[i] = istr.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !UTF8Encoding::isLegal((unsigned char*) buffer, count) )
|
||||||
|
{
|
||||||
|
throw JSONException("No legal UTF8 found");
|
||||||
|
}
|
||||||
|
buffer[count] = '\0';
|
||||||
|
_value += buffer;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ( c == '\\' ) // Escaped String
|
if ( c == '\\' ) // Escaped String
|
||||||
{
|
{
|
||||||
c = istr.get();
|
c = istr.get();
|
||||||
@ -196,8 +222,16 @@ public:
|
|||||||
{
|
{
|
||||||
throw JSONException("Invalid unicode");
|
throw JSONException("Invalid unicode");
|
||||||
}
|
}
|
||||||
c = unicode;
|
|
||||||
break;
|
Poco::UTF8Encoding utf8encoding;
|
||||||
|
int length = utf8encoding.convert(unicode, NULL, 0);
|
||||||
|
std::vector<unsigned char> convert(length);
|
||||||
|
utf8encoding.convert(unicode, &convert[0], length);
|
||||||
|
for(int i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
_value += (char) convert[i];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@ -206,7 +240,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_value += c;
|
_value += c;
|
||||||
c = istr.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( c == -1 )
|
if ( c == -1 )
|
||||||
@ -241,6 +274,49 @@ public:
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int utf8_check_first(char byte)
|
||||||
|
{
|
||||||
|
unsigned char u = (unsigned char) byte;
|
||||||
|
|
||||||
|
if(u < 0x80)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (0x80 <= u && u <= 0xBF)
|
||||||
|
{
|
||||||
|
/* second, third or fourth byte of a multi-byte
|
||||||
|
sequence, i.e. a "continuation byte" */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(u == 0xC0 || u == 0xC1)
|
||||||
|
{
|
||||||
|
/* overlong encoding of an ASCII byte */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(0xC2 <= u && u <= 0xDF)
|
||||||
|
{
|
||||||
|
/* 2-byte sequence */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else if(0xE0 <= u && u <= 0xEF)
|
||||||
|
{
|
||||||
|
/* 3-byte sequence */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
else if(0xF0 <= u && u <= 0xF4)
|
||||||
|
{
|
||||||
|
/* 4-byte sequence */
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* u >= 0xF5 */
|
||||||
|
/* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
|
||||||
|
UTF-8 */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -648,6 +724,8 @@ void Parser::readValue(const Token* token)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Token::INVALID_TOKEN:
|
||||||
|
throw JSONException(format("Invalid token '%s' found", token->asString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "JSONTest.h"
|
#include "JSONTest.h"
|
||||||
#include "CppUnit/TestCaller.h"
|
#include "CppUnit/TestCaller.h"
|
||||||
#include "CppUnit/TestSuite.h"
|
#include "CppUnit/TestSuite.h"
|
||||||
|
|
||||||
#include "Poco/JSON/Object.h"
|
#include "Poco/JSON/Object.h"
|
||||||
#include "Poco/JSON/Parser.h"
|
#include "Poco/JSON/Parser.h"
|
||||||
#include "Poco/JSON/Query.h"
|
#include "Poco/JSON/Query.h"
|
||||||
@ -40,13 +41,17 @@
|
|||||||
#include "Poco/JSON/Stringifier.h"
|
#include "Poco/JSON/Stringifier.h"
|
||||||
#include "Poco/JSON/DefaultHandler.h"
|
#include "Poco/JSON/DefaultHandler.h"
|
||||||
#include "Poco/JSON/Template.h"
|
#include "Poco/JSON/Template.h"
|
||||||
|
|
||||||
#include "Poco/Path.h"
|
#include "Poco/Path.h"
|
||||||
#include "Poco/Environment.h"
|
#include "Poco/Environment.h"
|
||||||
#include "Poco/File.h"
|
#include "Poco/File.h"
|
||||||
#include "Poco/FileStream.h"
|
#include "Poco/FileStream.h"
|
||||||
#include "Poco/Glob.h"
|
#include "Poco/Glob.h"
|
||||||
#include <set>
|
#include "Poco/UTF8Encoding.h"
|
||||||
|
#include "Poco/Latin1Encoding.h"
|
||||||
|
#include "Poco/TextConverter.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
using namespace Poco::JSON;
|
using namespace Poco::JSON;
|
||||||
using namespace Poco::Dynamic;
|
using namespace Poco::Dynamic;
|
||||||
@ -74,22 +79,6 @@ void JSONTest::tearDown()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSONTest::testStringifier()
|
|
||||||
{
|
|
||||||
Object obj;
|
|
||||||
|
|
||||||
Array arr;
|
|
||||||
Object obj2;
|
|
||||||
|
|
||||||
obj.set("array", arr);
|
|
||||||
obj.set("obj2", obj2);
|
|
||||||
|
|
||||||
std::ostringstream ostr;
|
|
||||||
obj.stringify(ostr);
|
|
||||||
assert (ostr.str() == "{\"array\":[],\"obj2\":{}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void JSONTest::testNullProperty()
|
void JSONTest::testNullProperty()
|
||||||
{
|
{
|
||||||
std::string json = "{ \"test\" : null }";
|
std::string json = "{ \"test\" : null }";
|
||||||
@ -845,6 +834,50 @@ void JSONTest::testInvalidJanssonFiles()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSONTest::testInvalidUnicodeJanssonFiles()
|
||||||
|
{
|
||||||
|
Poco::Path pathPattern(getTestFilesPath("invalid-unicode"));
|
||||||
|
|
||||||
|
std::set<std::string> paths;
|
||||||
|
Poco::Glob::glob(pathPattern, paths);
|
||||||
|
|
||||||
|
for(std::set<std::string>::iterator it = paths.begin(); it != paths.end(); ++it)
|
||||||
|
{
|
||||||
|
Poco::Path filePath(*it, "input");
|
||||||
|
|
||||||
|
if ( filePath.isFile() )
|
||||||
|
{
|
||||||
|
Poco::File inputFile(filePath);
|
||||||
|
if ( inputFile.exists() )
|
||||||
|
{
|
||||||
|
Poco::FileInputStream fis(filePath.toString());
|
||||||
|
std::cout << filePath.toString() << std::endl;
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
Var result;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DefaultHandler handler;
|
||||||
|
parser.setHandler(&handler);
|
||||||
|
parser.parse(fis);
|
||||||
|
result = handler.result();
|
||||||
|
// We shouldn't get here.
|
||||||
|
std::cout << "We didn't get an exception. This is the result: " << result.convert<std::string>() << std::endl;
|
||||||
|
fail(result.convert<std::string>());
|
||||||
|
}
|
||||||
|
catch(JSONException&)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
catch(Poco::SyntaxException&)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSONTest::testTemplate()
|
void JSONTest::testTemplate()
|
||||||
{
|
{
|
||||||
Template tpl;
|
Template tpl;
|
||||||
@ -858,6 +891,40 @@ void JSONTest::testTemplate()
|
|||||||
tpl.render(data, std::cout);
|
tpl.render(data, std::cout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JSONTest::testUnicode()
|
||||||
|
{
|
||||||
|
const unsigned char supp[] = {0x61, 0xE1, 0xE9, 0x78, 0xED, 0xF3, 0xFA, 0x0};
|
||||||
|
std::string text((const char*) supp);
|
||||||
|
|
||||||
|
std::string json = "{ \"test\" : \"a\\u00E1\\u00E9x\\u00ED\\u00F3\\u00FA\" }";
|
||||||
|
Parser parser;
|
||||||
|
|
||||||
|
Var result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DefaultHandler handler;
|
||||||
|
parser.setHandler(&handler);
|
||||||
|
parser.parse(json);
|
||||||
|
result = handler.result();
|
||||||
|
}
|
||||||
|
catch(JSONException& jsone)
|
||||||
|
{
|
||||||
|
std::cout << jsone.message() << std::endl;
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
assert(result.type() == typeid(Object::Ptr));
|
||||||
|
|
||||||
|
Object::Ptr object = result.extract<Object::Ptr>();
|
||||||
|
Var test = object->get("test");
|
||||||
|
|
||||||
|
Poco::Latin1Encoding latin1;
|
||||||
|
Poco::UTF8Encoding utf8;
|
||||||
|
Poco::TextConverter converter(latin1, utf8);
|
||||||
|
std::string original;
|
||||||
|
converter.convert(text, original);
|
||||||
|
|
||||||
|
assert(test.convert<std::string>() == original);
|
||||||
|
}
|
||||||
|
|
||||||
std::string JSONTest::getTestFilesPath(const std::string& type)
|
std::string JSONTest::getTestFilesPath(const std::string& type)
|
||||||
{
|
{
|
||||||
@ -879,8 +946,10 @@ std::string JSONTest::getTestFilesPath(const std::string& type)
|
|||||||
if (Poco::File(pathPattern).exists())
|
if (Poco::File(pathPattern).exists())
|
||||||
validDir += '*';
|
validDir += '*';
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Can't find " << validDir << std::endl;
|
||||||
throw Poco::NotFoundException("cannot locate directory containing valid JSON test files");
|
throw Poco::NotFoundException("cannot locate directory containing valid JSON test files");
|
||||||
|
}
|
||||||
return validDir;
|
return validDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,7 +958,6 @@ CppUnit::Test* JSONTest::suite()
|
|||||||
{
|
{
|
||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("JSONTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("JSONTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, JSONTest, testStringifier);
|
|
||||||
CppUnit_addTest(pSuite, JSONTest, testNullProperty);
|
CppUnit_addTest(pSuite, JSONTest, testNullProperty);
|
||||||
CppUnit_addTest(pSuite, JSONTest, testTrueProperty);
|
CppUnit_addTest(pSuite, JSONTest, testTrueProperty);
|
||||||
CppUnit_addTest(pSuite, JSONTest, testFalseProperty);
|
CppUnit_addTest(pSuite, JSONTest, testFalseProperty);
|
||||||
@ -917,7 +985,9 @@ CppUnit::Test* JSONTest::suite()
|
|||||||
CppUnit_addTest(pSuite, JSONTest, testQuery);
|
CppUnit_addTest(pSuite, JSONTest, testQuery);
|
||||||
CppUnit_addTest(pSuite, JSONTest, testValidJanssonFiles);
|
CppUnit_addTest(pSuite, JSONTest, testValidJanssonFiles);
|
||||||
CppUnit_addTest(pSuite, JSONTest, testInvalidJanssonFiles);
|
CppUnit_addTest(pSuite, JSONTest, testInvalidJanssonFiles);
|
||||||
|
CppUnit_addTest(pSuite, JSONTest, testInvalidUnicodeJanssonFiles);
|
||||||
CppUnit_addTest(pSuite, JSONTest, testTemplate);
|
CppUnit_addTest(pSuite, JSONTest, testTemplate);
|
||||||
|
CppUnit_addTest(pSuite, JSONTest, testUnicode);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ public:
|
|||||||
JSONTest(const std::string& name);
|
JSONTest(const std::string& name);
|
||||||
~JSONTest();
|
~JSONTest();
|
||||||
|
|
||||||
void testStringifier();
|
|
||||||
void testNullProperty();
|
void testNullProperty();
|
||||||
void testTrueProperty();
|
void testTrueProperty();
|
||||||
void testFalseProperty();
|
void testFalseProperty();
|
||||||
@ -76,6 +75,8 @@ public:
|
|||||||
void testInvalidJanssonFiles();
|
void testInvalidJanssonFiles();
|
||||||
void testTemplate();
|
void testTemplate();
|
||||||
void testItunes();
|
void testItunes();
|
||||||
|
void testUnicode();
|
||||||
|
void testInvalidUnicodeJanssonFiles();
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
void tearDown();
|
void tearDown();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user