#include "bu/json.h" #include "bu/staticmembuf.h" #include #define next( txt ) readChar( c, sInput, "Unexpected end of stream while reading " txt "." ) Bu::Json::Json() : eType( Invalid ) { } Bu::Json::Json( const Bu::String &sJson ) : eType( Invalid ) { Bu::StaticMemBuf mIn( sJson.getStr(), sJson.getSize() ); parse( mIn ); } Bu::Json::Json( Bu::Stream &sInput ) : eType( Invalid ) { parse( sInput ); } Bu::Json::~Json() { reset(); } void Bu::Json::parse( Bu::Stream &sInput ) { reset(); char c; for(;;) { next( "json" ); if( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) continue; if( c == '"' ) { // String parseString( c, sInput ); } else if( c == '{' ) { // Object parseObject( c, sInput ); } else if( c == '[' ) { // Array parseArray( c, sInput ); } else if( c == '-' || (c >= '0' && c <= '9') ) { // Number -- apparently they can't start with a period parseNumber( c, sInput ); } else if( c == 't' || c == 'f' || c == 'n' ) { // True / false / null parseLiteral( c, sInput ); } } } void Bu::Json::reset() { switch( eType ) { case Object: for( JsonHash::iterator i = uDat.pObject->begin(); i; i++ ) { delete i.getValue(); } delete uDat.pObject; break; case Array: for( JsonList::iterator i = uDat.pArray->begin(); i; i++ ) { delete *i; } delete uDat.pArray; break; case String: delete uDat.pString; break; case Invalid: case Number: case Boolean: case Null: break; } uDat.pObject = NULL; } void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) { while( c != '"' ) { next( "string" ); } bool bEscape = false; for(;;) { next( "string" ); if( bEscape ) { switch( c ) { case '"': case '\\': case '/': sOut += c; break; case 'b': sOut += '\b'; break; case 'f': sOut += '\f'; break; case 'n': sOut += '\n'; break; case 'r': sOut += '\r'; break; case 't': sOut += '\t'; break; case 'u': // Not implimented yet, followed by four hex diigts break; default: throw Bu::ExceptionBase( "Invalid escape sequence encountered in string." ); break; } bEscape = false; } else { if( c == '\\' ) bEscape = true; if( c == '"' ) { break; } sOut += c; } } } void Bu::Json::parseString( char &c, Bu::Stream &sInput ) { eType = String; uDat.pString = new Bu::String(); parseString( c, sInput, *uDat.pString ); } void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) { while( c != '{' ) { next( "object" ); } eType = Object; uDat.pObject = new JsonHash(); next( "object" ); for(;;) { Bu::String sKey; parseString( c, sInput, sKey ); next( "object" ); if( c != ':' ) { throw Bu::ExceptionBase( "Invalid json, expected colon after key in object." ); } uDat.pObject->insert( sKey, new Json( sInput ) ); next( "object" ); if( c == '}' ) break; else if( c == ',' ) next( "object" ); else throw Bu::ExceptionBase( "Invalid json, expected comma or } after value in object." ); } } void Bu::Json::parseArray( char &c, Bu::Stream &sInput ) { while( c != '[' ) { next( "array" ); } eType = Array; uDat.pArray = new JsonList(); next( "array" ); for(;;) { uDat.pArray->append( new Json( sInput ) ); next( "array" ); if( c == ']' ) break; else if( c == ',' ) continue; else { throw Bu::ExceptionBase( "Invalid json, expected comma or ] after value in array." ); } } } void Bu::Json::parseNumber( char &c, Bu::Stream &sInput ) { while( c != '-' && c < '0' && c > '9' ) { next( "number" ); } Bu::String sBuf; if( c == '-' ) { sBuf += c; next( "number" ); } bool bIntPart = true; do { if( c >= '0' && c <= '9' ) sBuf += c; else if( c == '.' && bIntPart == true ) { bIntPart = false; sBuf += c; } else if( c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '}' || c == ']' || c == ',' ) { break; } else { throw Bu::ExceptionBase("Invalid character in number."); } } while( readChar( c, sInput ) ); eType = Number; uDat.dNumber = atof( sBuf.getStr() ); } void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput ) { while( c != 'f' && c != 't' && c != 'n' ) { next( "literal" ); } Bu::String s; do { if( isWs( c ) || c == ',' || c == '}' || c == ']' ) break; else s += c; } while( readChar( c, sInput ) ); if( s == "true" ) { eType = Boolean; uDat.bBoolean = true; } else if( s == "false" ) { eType = Boolean; uDat.bBoolean = false; } else if( s == "null" ) { eType = Null; uDat.pObject = NULL; } else { throw Bu::ExceptionBase("Invalid literal token found."); } } bool Bu::Json::readChar( char &c, Bu::Stream &sInput ) { if( sInput.read( &c, 1 ) == 0 ) return false; return true; } void Bu::Json::readChar( char &c, Bu::Stream &sInput, const char *sSection ) { if( sInput.read( &c, 1 ) == 0 ) { throw Bu::ExceptionBase( sSection ); } } bool Bu::Json::isWs( char c ) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; }