JSON fixes/refactoring

This commit is contained in:
Aleksandar Fabijanic 2013-05-20 23:36:58 -05:00
parent 9b6db2e75d
commit 41a07c15bf
6 changed files with 225 additions and 143 deletions

View File

@ -48,55 +48,55 @@
#if defined(_WIN32_WINNT_WIN8) #if defined(_WIN32_WINNT_WIN8)
//Windows 8 _WIN32_WINNT_WIN8 (0x0602) //Windows 8 _WIN32_WINNT_WIN8 (0x0602)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WIN8 #define _WIN32_WINNT _WIN32_WINNT_WIN8
#elif defined(_WIN32_WINNT_WIN7) #elif defined(_WIN32_WINNT_WIN7)
//Windows 7 _WIN32_WINNT_WIN7 (0x0601) //Windows 7 _WIN32_WINNT_WIN7 (0x0601)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WIN7 #define _WIN32_WINNT _WIN32_WINNT_WIN7
#elif defined (_WIN32_WINNT_WS08) #elif defined (_WIN32_WINNT_WS08)
//Windows Server 2008 _WIN32_WINNT_WS08 (0x0600) //Windows Server 2008 _WIN32_WINNT_WS08 (0x0600)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WS08 #define _WIN32_WINNT _WIN32_WINNT_WS08
#elif defined (_WIN32_WINNT_VISTA) #elif defined (_WIN32_WINNT_VISTA)
//Windows Vista _WIN32_WINNT_VISTA (0x0600) //Windows Vista _WIN32_WINNT_VISTA (0x0600)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_VISTA #define _WIN32_WINNT _WIN32_WINNT_VISTA
#elif defined (_WIN32_WINNT_LONGHORN) #elif defined (_WIN32_WINNT_LONGHORN)
//Windows Vista and server 2008 Development _WIN32_WINNT_LONGHORN (0x0600) //Windows Vista and server 2008 Development _WIN32_WINNT_LONGHORN (0x0600)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_LONGHORN #define _WIN32_WINNT _WIN32_WINNT_LONGHORN
#elif defined (_WIN32_WINNT_WS03) #elif defined (_WIN32_WINNT_WS03)
//Windows Server 2003 with SP1, //Windows Server 2003 with SP1,
//Windows XP with SP2 _WIN32_WINNT_WS03 (0x0502) //Windows XP with SP2 _WIN32_WINNT_WS03 (0x0502)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WS03 #define _WIN32_WINNT _WIN32_WINNT_WS03
#elif defined (_WIN32_WINNT_WINXP) #elif defined (_WIN32_WINNT_WINXP)
//Windows Server 2003, Windows XP _WIN32_WINNT_WINXP (0x0501) //Windows Server 2003, Windows XP _WIN32_WINNT_WINXP (0x0501)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WINXP #define _WIN32_WINNT _WIN32_WINNT_WINXP
#elif defined (_WIN32_WINNT_WIN2K) #elif defined (_WIN32_WINNT_WIN2K)
//Windows 2000 _WIN32_WINNT_WIN2K (0x0500) //Windows 2000 _WIN32_WINNT_WIN2K (0x0500)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT _WIN32_WINNT_WIN2K #define _WIN32_WINNT _WIN32_WINNT_WIN2K
#elif defined (WINVER) #elif defined (WINVER)
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT WINVER #define _WIN32_WINNT WINVER
#endif #endif

View File

@ -53,64 +53,9 @@
namespace Poco { namespace Poco {
namespace JSON { namespace JSON {
class JSON_API CharacterFeeder
{
public:
virtual ~CharacterFeeder() {}
virtual bool nextChar(int& c) = 0;
};
class Source;
class JSON_API StreamCharacterFeeder : public CharacterFeeder
{
public:
StreamCharacterFeeder(std::istream& in) : _in(in) {}
virtual ~StreamCharacterFeeder() {}
bool nextChar(int& c);
private:
std::istream& _in;
};
inline bool StreamCharacterFeeder::nextChar(int& c)
{
if ( _in.good() )
{
c = _in.get();
return _in.good();
}
return false;
}
class JSON_API StringCharacterFeeder : public CharacterFeeder
{
public:
StringCharacterFeeder(std::string::const_iterator begin, std::string::const_iterator end) : _it(begin), _end(end)
{
}
virtual ~StringCharacterFeeder() {}
bool nextChar(int& c);
private:
std::string::const_iterator _it;
std::string::const_iterator _end;
};
inline bool StringCharacterFeeder::nextChar(int& c)
{
if ( _it == _end )
return false;
c = *_it++;
return true;
}
class JSON_API Parser class JSON_API Parser
/// A class for passing JSON strings or streams. /// A class for passing JSON strings or streams.
@ -239,23 +184,39 @@ public:
JSON_T_VALUE_SEPARATOR, JSON_T_VALUE_SEPARATOR,
JSON_T_MAX JSON_T_MAX
}; };
static const std::size_t PARSE_BUFFER_SIZE = 3500;
static const std::size_t PARSER_STACK_SIZE = 128;
static const int UNLIMITED_DEPTH = -1;
Parser(const Handler::Ptr& pHandler = new ParseHandler, std::size_t bufSize = PARSE_BUFFER_SIZE);
static const int PARSE_BUFFER_SIZE = 3500;
static const int PARSER_STACK_SIZE = 128;
Parser(const Handler::Ptr& pHandler = new ParseHandler);
/// Constructor. /// Constructor.
virtual ~Parser(); virtual ~Parser();
/// Destructor. /// Destructor.
void reset();
/// Resets the parser.
void allowComments(bool sw); void setAllowComments(bool comments);
/// Allow comments. By default this is false. /// Allow comments. By default, comments are not allowed.
bool getAllowComments() const;
/// Returns true if comments are allowed, false otherwise.
/// By default, comments are not allowed.
void allowNullByte(bool sw); void setAllowNullByte(bool nullByte);
/// Allow a null byte in strings. By default this is true. /// Allow null byte in strings. By default, null byte is allowed.
bool getAllowNullByte() const;
/// Returns true if null byte is allowed, false otherwise.
/// By default, null bytes are allowed.
void setDepth(std::size_t depth);
/// Sets the allowed JSON depth.
std::size_t getDepth() const;
/// Returns the allowed JSON depth.
Dynamic::Var parse(const std::string& json); Dynamic::Var parse(const std::string& json);
/// Parses a string. /// Parses a string.
@ -273,12 +234,8 @@ public:
/// Returns the result of parsing; /// Returns the result of parsing;
private: private:
Handler::Ptr _pHandler;
typedef Poco::Buffer<char> BufType; typedef Poco::Buffer<char> BufType;
bool push(int mode); bool push(int mode);
/// Push a mode onto the _pStack. Return false if there is overflow. /// Push a mode onto the _pStack. Return false if there is overflow.
@ -288,83 +245,95 @@ private:
void growBuffer(); void growBuffer();
void clearBuffer(); void clearBuffer();
void parseBufferPushBackChar(char c); void parseBufferPushBackChar(char c);
void parseBufferPopBackChar(); void parseBufferPopBackChar();
void addCharToParseBuffer(int nextChar, int nextClass); void addCharToParseBuffer(int nextChar, int nextClass);
void addEscapedCharToParseBuffer(int nextChar); void addEscapedCharToParseBuffer(int nextChar);
int decodeUnicodeChar(); int decodeUnicodeChar();
void assertNotStringNullBool(); void assertNotStringNullBool();
void assertNonContainer(); void assertNonContainer();
void parseBuffer(); void parseBuffer();
bool parseChar(int nextChar, Source& feeder);
bool parseChar(int nextChar);
/// Called for each character (or partial character) in JSON string. /// Called for each character (or partial character) in JSON string.
/// It accepts UTF-8, UTF-16, or UTF-32. If it the character is accpeted, /// It accepts UTF-8, UTF-16, or UTF-32. If it the character is accepted,
/// it returns true, otherwise false. /// it returns true, otherwise false.
bool done(); bool done();
static int utf8_check_first(char byte); static int utf8_check_first(char byte);
static const int _asciiClass[128]; static const int _asciiClass[128];
/// This array maps the 128 ASCII characters into character classes. /// This array maps the 128 ASCII characters into character classes.
/// The remaining Unicode characters should be mapped to C_ETC. /// The remaining Unicode characters should be mapped to C_ETC.
/// Non-whitespace control characters are errors. /// Non-whitespace control characters are errors.
static const int _stateTransitionTable[NR_STATES][NR_CLASSES]; static const int _stateTransitionTable[NR_STATES][NR_CLASSES];
static const int xx = -1; static const int xx = -1;
signed char _state; Handler::Ptr _pHandler;
signed char _beforeCommentState; signed char _state;
JSONType _type; signed char _beforeCommentState;
signed char _escaped; JSONType _type;
signed char _comment; signed char _escaped;
signed char _comment;
unsigned short _utf16HighSurrogate; unsigned short _utf16HighSurrogate;
long _depth; int _depth;
long _top; int _top;
BufType _stack; BufType _stack;
BufType _parseBuffer; BufType _parseBuffer;
size_t _parseBufferCount; std::size_t _parseBufferCount;
size_t _commentBeginOffset; char _decimalPoint;
char _decimalPoint; bool _allowNullByte;
bool _allowNullByte; bool _allowComments;
bool _allowComments;
SharedPtr<CharacterFeeder> feeder;
}; };
inline void Parser::allowComments(bool sw) inline void Parser::setAllowComments(bool sw)
{ {
_allowComments = sw; _allowComments = sw;
} }
inline void Parser::allowNullByte(bool sw) inline bool Parser::getAllowComments() const
{
return _allowComments;
}
inline void Parser::setAllowNullByte(bool sw)
{ {
_allowNullByte = sw; _allowNullByte = sw;
} }
inline bool Parser::getAllowNullByte() const
{
return _allowNullByte;
}
inline void Parser::setDepth(std::size_t depth)
{
_depth = depth;
}
inline std::size_t Parser::getDepth() const
{
return _depth;
}
inline void Parser::setHandler(const Handler::Ptr& pHandler) inline void Parser::setHandler(const Handler::Ptr& pHandler)
{ {
_pHandler = pHandler; _pHandler = pHandler;
@ -385,7 +354,7 @@ inline Dynamic::Var Parser::result() const
inline bool Parser::done() inline bool Parser::done()
{ {
return _state == OK && pop(MODE_DONE); return _state == OK && pop(MODE_DONE);
} }
@ -414,6 +383,7 @@ inline void Parser::growBuffer()
_parseBuffer.resize(_parseBuffer.size() * 2, true); _parseBuffer.resize(_parseBuffer.size() * 2, true);
} }
}} // namespace Poco::JSON }} // namespace Poco::JSON

View File

@ -125,11 +125,13 @@ private:
const char* endLine() const; const char* endLine() const;
unsigned indent(); unsigned indent();
bool printFlat() const; bool printFlat() const;
void arrayValue();
std::ostream& _out; std::ostream& _out;
unsigned _indent; unsigned _indent;
std::string _tab; std::string _tab;
bool _array; bool _array;
bool _value;
}; };

View File

@ -129,7 +129,76 @@ const int Parser::_stateTransitionTable[NR_STATES][NR_CLASSES] = {
}; };
Parser::Parser(const Handler::Ptr& pHandler) : // Source
class Source
{
public:
Source()
{
}
virtual ~Source()
{
}
virtual bool nextChar(int& c) = 0;
};
class StreamSource : public Source
{
public:
StreamSource(std::istream& in) : _in(in)
{
}
virtual ~StreamSource()
{
}
bool nextChar(int& c)
{
if ( _in.good() )
{
c = _in.get();
return _in.good();
}
return false;
}
private:
std::istream& _in;
};
class StringSource : public Source
{
public:
StringSource(std::string::const_iterator begin, std::string::const_iterator end) : _it(begin), _end(end)
{
}
virtual ~StringSource()
{
}
bool nextChar(int& c)
{
if ( _it == _end ) return false;
c = *_it++;
return true;
}
private:
std::string::const_iterator _it;
std::string::const_iterator _end;
};
// Parser
Parser::Parser(const Handler::Ptr& pHandler, std::size_t bufSize) :
_pHandler(pHandler), _pHandler(pHandler),
_state(GO), _state(GO),
_beforeCommentState(0), _beforeCommentState(0),
@ -137,38 +206,50 @@ Parser::Parser(const Handler::Ptr& pHandler) :
_escaped(0), _escaped(0),
_comment(0), _comment(0),
_utf16HighSurrogate(0), _utf16HighSurrogate(0),
//_depth(config.parseDepth() ? config.parseDepth() : -1), _depth(UNLIMITED_DEPTH),
_depth(-1),
_top(-1), _top(-1),
_stack(PARSER_STACK_SIZE), _stack(PARSER_STACK_SIZE),
_parseBuffer(PARSE_BUFFER_SIZE), _parseBuffer(bufSize),
_parseBufferCount(0), _parseBufferCount(0),
_commentBeginOffset(0), _decimalPoint('.'),
_decimalPoint(0),
_allowNullByte(true), _allowNullByte(true),
_allowComments(false) _allowComments(false)
{ {
_stack.clear();
_parseBuffer.clear();
push(MODE_DONE); push(MODE_DONE);
_decimalPoint = *std::localeconv()->decimal_point;
clearBuffer();
} }
Parser::~Parser() Parser::~Parser()
{ {
} }
void Parser::reset()
{
_state = GO;
_beforeCommentState = 0;
_type = JSON_T_NONE;
_escaped = 0;
_comment = 0;
_utf16HighSurrogate = 0;
_top = -1;
_parseBufferCount = 0;
_stack.clear();
_parseBuffer.clear();
push(MODE_DONE);
clearBuffer();
}
Dynamic::Var Parser::parse(const std::string& json) Dynamic::Var Parser::parse(const std::string& json)
{ {
feeder = new StringCharacterFeeder(json.begin(), json.end()); StringSource source(json.begin(), json.end());
int c = 0; int c = 0;
while(feeder->nextChar(c)) while(source.nextChar(c))
{ {
if (0 == parseChar(c)) throw SyntaxException("JSON syntax error"); if (0 == parseChar(c, source)) throw SyntaxException("JSON syntax error");
} }
if (!done()) if (!done())
@ -180,12 +261,12 @@ Dynamic::Var Parser::parse(const std::string& json)
Dynamic::Var Parser::parse(std::istream& in) Dynamic::Var Parser::parse(std::istream& in)
{ {
feeder = new StreamCharacterFeeder(in); StreamSource source(in);
int c = 0; int c = 0;
while(feeder->nextChar(c)) while(source.nextChar(c))
{ {
if (0 == parseChar(c)) throw JSONException("JSON syntax error"); if (0 == parseChar(c, source)) throw JSONException("JSON syntax error");
} }
if (!done()) if (!done())
@ -222,6 +303,7 @@ bool Parser::pop(int mode)
return true; return true;
} }
void Parser::clearBuffer() void Parser::clearBuffer()
{ {
_parseBufferCount = 0; _parseBufferCount = 0;
@ -305,7 +387,7 @@ void Parser::addCharToParseBuffer(int nextChar, int nextClass)
} }
bool Parser::parseChar(int nextChar) bool Parser::parseChar(int nextChar, Source& source)
{ {
int nextClass, nextState; int nextClass, nextState;
@ -325,7 +407,7 @@ bool Parser::parseChar(int nextChar)
for(int i = 1; i < count; ++i) for(int i = 1; i < count; ++i)
{ {
int c = 0; int c = 0;
if ( !feeder->nextChar(c) ) throw JSONException("Invalid UTF8 character found"); if (!source.nextChar(c)) throw JSONException("Invalid UTF8 character found");
buffer[i] = c; buffer[i] = c;
} }
@ -702,8 +784,8 @@ void Parser::parseBuffer()
{ {
Int64 value = NumberParser::parse64(_parseBuffer.begin()); Int64 value = NumberParser::parse64(_parseBuffer.begin());
// if number is 32-bit, then handle as such // if number is 32-bit, then handle as such
if ( value > std::numeric_limits<int>::max() if (value > std::numeric_limits<int>::max()
|| value < std::numeric_limits<int>::min() ) || value < std::numeric_limits<int>::min() )
{ {
_pHandler->value(value); _pHandler->value(value);
} }

View File

@ -46,7 +46,8 @@ namespace JSON {
PrintHandler::PrintHandler(unsigned indent): PrintHandler::PrintHandler(unsigned indent):
_out(std::cout), _out(std::cout),
_indent(indent), _indent(indent),
_array(false) _array(false),
_value(false)
{ {
} }
@ -54,7 +55,8 @@ PrintHandler::PrintHandler(unsigned indent):
PrintHandler::PrintHandler(std::ostream& out, unsigned indent): PrintHandler::PrintHandler(std::ostream& out, unsigned indent):
_out(out), _out(out),
_indent(indent), _indent(indent),
_array(false) _array(false),
_value(false)
{ {
} }
@ -107,6 +109,7 @@ void PrintHandler::startArray()
_out << '[' << endLine(); _out << '[' << endLine();
_tab.append(indent(), ' '); _tab.append(indent(), ' ');
_array = true; _array = true;
_value = false;
} }
@ -115,11 +118,17 @@ void PrintHandler::endArray()
_tab.erase(_tab.length() - indent()); _tab.erase(_tab.length() - indent());
_out << endLine() << _tab << ']'; _out << endLine() << _tab << ']';
_array = false; _array = false;
_value = false;
} }
void PrintHandler::key(const std::string& k) void PrintHandler::key(const std::string& k)
{ {
if (_value)
{
comma();
_value = false;
}
_out << _tab << '"' << k << '"'; _out << _tab << '"' << k << '"';
if (!printFlat()) _out << ' '; if (!printFlat()) _out << ' ';
_out << ':'; _out << ':';
@ -129,60 +138,68 @@ void PrintHandler::key(const std::string& k)
void PrintHandler::null() void PrintHandler::null()
{ {
if (_array) _out << _tab; arrayValue();
_out << "null"; _out << "null";
_value = true;
} }
void PrintHandler::value(int v) void PrintHandler::value(int v)
{ {
if (_array) _out << _tab; arrayValue();
_out << v; _out << v;
_value = true;
} }
void PrintHandler::value(unsigned v) void PrintHandler::value(unsigned v)
{ {
if (_array) _out << _tab; arrayValue();
_out << v; _out << v;
_value = true;
} }
#if defined(POCO_HAVE_INT64) #if defined(POCO_HAVE_INT64)
void PrintHandler::value(Int64 v) void PrintHandler::value(Int64 v)
{ {
if (_array) _out << _tab; arrayValue();
_out << v; _out << v;
_value = true;
} }
void PrintHandler::value(UInt64 v) void PrintHandler::value(UInt64 v)
{ {
if (_array) _out << _tab; arrayValue();
_out << v; _out << v;
_value = true;
} }
#endif #endif
void PrintHandler::value(const std::string& value) void PrintHandler::value(const std::string& value)
{ {
if (_array) _out << _tab; arrayValue();
Stringifier::formatString(value, _out); Stringifier::formatString(value, _out);
_value = true;
} }
void PrintHandler::value(double d) void PrintHandler::value(double d)
{ {
if (_array) _out << _tab; arrayValue();
_out << d; _out << d;
_value = true;
} }
void PrintHandler::value(bool b) void PrintHandler::value(bool b)
{ {
if (_array) _out << _tab; arrayValue();
_out << b; _out << b;
_value = true;
} }
@ -192,4 +209,12 @@ void PrintHandler::comma()
} }
void PrintHandler::arrayValue()
{
if (_array)
{
if (_value) comma();
_out << _tab;
}
}
} } // namespace Poco::JSON } } // namespace Poco::JSON

View File

@ -841,6 +841,7 @@ void JSONTest::testPrintHandler()
pHandler->setIndent(1); pHandler->setIndent(1);
ostr.str(""); ostr.str("");
parser.reset();
parser.parse(json); parser.parse(json);
assert (ostr.str() == "{\n" assert (ostr.str() == "{\n"
" \"name\" : \"Homer\",\n" " \"name\" : \"Homer\",\n"
@ -857,6 +858,7 @@ void JSONTest::testPrintHandler()
pHandler->setIndent(2); pHandler->setIndent(2);
ostr.str(""); ostr.str("");
parser.reset();
parser.parse(json); parser.parse(json);
assert (ostr.str() == "{\n" assert (ostr.str() == "{\n"
" \"name\" : \"Homer\",\n" " \"name\" : \"Homer\",\n"
@ -873,6 +875,7 @@ void JSONTest::testPrintHandler()
pHandler->setIndent(4); pHandler->setIndent(4);
ostr.str(""); ostr.str("");
parser.reset();
parser.parse(json); parser.parse(json);
assert (ostr.str() == "{\n" assert (ostr.str() == "{\n"
" \"name\" : \"Homer\",\n" " \"name\" : \"Homer\",\n"
@ -1193,7 +1196,7 @@ void JSONTest::testInvalidJanssonFiles()
std::cout << filePath.toString() << std::endl; std::cout << filePath.toString() << std::endl;
Parser parser; Parser parser;
parser.allowNullByte(false); parser.setAllowNullByte(false);
Var result; Var result;
try try
@ -1236,7 +1239,7 @@ void JSONTest::testInvalidUnicodeJanssonFiles()
std::cout << filePath.toString() << std::endl; std::cout << filePath.toString() << std::endl;
Parser parser; Parser parser;
parser.allowNullByte(false); parser.setAllowNullByte(false);
Var result; Var result;
try try