From dc759b40b9a18b20454b68244dd063ab56155c3e Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 6 Jun 2017 22:45:21 -0700 Subject: Reading, writing, and accessing loaded data all work. --- src/unstable/json.cpp | 364 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 310 insertions(+), 54 deletions(-) (limited to 'src/unstable/json.cpp') diff --git a/src/unstable/json.cpp b/src/unstable/json.cpp index c31c7f9..0b47da4 100644 --- a/src/unstable/json.cpp +++ b/src/unstable/json.cpp @@ -1,5 +1,8 @@ #include "bu/json.h" #include "bu/staticmembuf.h" +#include "bu/membuf.h" + +#include "bu/sio.h" #include @@ -23,46 +26,146 @@ Bu::Json::Json( Bu::Stream &sInput ) : parse( sInput ); } +Bu::Json::Json( char &c, Bu::Stream &sInput ) : + eType( Invalid ) +{ + parse( c, sInput ); +} + Bu::Json::~Json() { reset(); } +Bu::Json::Type Bu::Json::getType() const +{ + return eType; +} + +Bu::String Bu::Json::getString() const +{ + if( eType != String ) + throw Bu::ExceptionBase( + "String requested from non-string json object." + ); + + return *uDat.pString; +} + +double Bu::Json::getNumber() const +{ + if( eType != Number ) + throw Bu::ExceptionBase( + "Number requested from non-number json object." + ); + + return uDat.dNumber; +} + +bool Bu::Json::getBoolean() const +{ + if( eType != Boolean ) + throw Bu::ExceptionBase( + "Boolean requested from non-boolean json object." + ); + + return uDat.bBoolean; +} + +bool Bu::Json::isNull() const +{ + return eType == Null; +} + +Bu::Json *Bu::Json::operator[]( const Bu::String &sKey ) const +{ + if( eType != Boolean ) + throw Bu::ExceptionBase( + "Object entry requested from non-object json object." + ); + + return uDat.pObject->get( sKey ); + +} + +int Bu::Json::getSize() const +{ + if( eType == Object ) + return uDat.pObject->getSize(); + else if( eType == Array ) + return uDat.pArray->getSize(); + else if( eType == String ) + return uDat.pString->getSize(); + else + throw Bu::ExceptionBase( + "Size requseted from json type that doesn't support it." + ); +} + +Bu::Json::iterator Bu::Json::begin() +{ + return uDat.pArray->begin(); +} + +Bu::Json::const_iterator Bu::Json::begin() const +{ + return uDat.pArray->begin(); +} + +Bu::Json::iterator Bu::Json::end() +{ + return uDat.pArray->end(); +} + +Bu::Json::const_iterator Bu::Json::end() const +{ + return uDat.pArray->end(); +} + void Bu::Json::parse( Bu::Stream &sInput ) { reset(); char c; - for(;;) + next("json"); + + parse( c, sInput ); +} + +void Bu::Json::parse( char &c, Bu::Stream &sInput ) +{ + while( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) { 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 ); - } + } + 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 ); + } + else + { + throw Bu::ExceptionBase("Invalid characters in json stream."); } } @@ -99,12 +202,112 @@ void Bu::Json::reset() uDat.pObject = NULL; } -void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) +void Bu::Json::write( Bu::Stream &sOutput ) const { - while( c != '"' ) + switch( eType ) { - next( "string" ); + case Invalid: + throw Bu::ExceptionBase("Invalid type in json"); + break; + + case Object: + { + sOutput.write("{", 1 ); + bool bFirst = true; + for( JsonHash::iterator i = uDat.pObject->begin(); i; i++ ) + { + if( bFirst == true ) + bFirst = false; + else + sOutput.write(","); + writeStr( i.getKey(), sOutput ); + sOutput.write(":", 1 ); + (*i)->write( sOutput ); + } + sOutput.write("}", 1 ); + } + break; + + case Array: + { + sOutput.write("[", 1); + bool bFirst = true; + for( JsonList::iterator i = uDat.pArray->begin(); i; i++ ) + { + if( bFirst == true ) + bFirst = false; + else + sOutput.write(",", 1 ); + (*i)->write( sOutput ); + } + sOutput.write("]", 1); + } + break; + + case String: + writeStr( *uDat.pString, sOutput ); + break; + + case Number: + sOutput.write(Bu::String("%1").arg( uDat.dNumber )); + break; + + case Boolean: + if( uDat.bBoolean ) + sOutput.write("true", 4 ); + else + sOutput.write("false", 5 ); + break; + + case Null: + sOutput.write("null", 4 ); + break; } +} + +void Bu::Json::writeStable( Bu::Stream &sOutput ) const +{ + if( eType == Object ) + { + sOutput.write("{", 1 ); + bool bFirst = true; + Bu::List lKey = uDat.pObject->getKeys(); + lKey.sort(); + for( Bu::List::iterator i = lKey.begin(); i; i++ ) + { + if( bFirst == true ) + bFirst = false; + else + sOutput.write(","); + writeStr( *i, sOutput ); + sOutput.write(":", 1 ); + uDat.pObject->get( *i )->write( sOutput ); + } + sOutput.write("}", 1 ); + } + else + { + write( sOutput ); + } +} + +Bu::String Bu::Json::toString() const +{ + Bu::MemBuf mb; + write( mb ); + return mb.getString(); +} + +Bu::String Bu::Json::toStringStable() const +{ + Bu::MemBuf mb; + writeStable( mb ); + return mb.getString(); +} + +void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) +{ + skipWs( c, sInput ); bool bEscape = false; for(;;) { @@ -157,6 +360,7 @@ void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) bEscape = true; if( c == '"' ) { + readChar( c, sInput ); break; } sOut += c; @@ -173,11 +377,7 @@ void Bu::Json::parseString( char &c, Bu::Stream &sInput ) void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) { - while( c != '{' ) - { - next( "object" ); - } - + skipWs( c, sInput ); eType = Object; uDat.pObject = new JsonHash(); @@ -186,17 +386,21 @@ void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) { Bu::String sKey; parseString( c, sInput, sKey ); - next( "object" ); + skipWs( c, sInput ); if( c != ':' ) { throw Bu::ExceptionBase( "Invalid json, expected colon after key in object." ); } - uDat.pObject->insert( sKey, new Json( sInput ) ); - next( "object" ); + next("object"); + uDat.pObject->insert( sKey, new Json( c, sInput ) ); + skipWs( c, sInput ); if( c == '}' ) + { + readChar( c, sInput ); break; + } else if( c == ',' ) next( "object" ); else @@ -208,23 +412,27 @@ void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) void Bu::Json::parseArray( char &c, Bu::Stream &sInput ) { - while( c != '[' ) - { - next( "array" ); - } + skipWs( c, sInput ); eType = Array; uDat.pArray = new JsonList(); - next( "array" ); + next("array"); + for(;;) { - uDat.pArray->append( new Json( sInput ) ); - next( "array" ); + uDat.pArray->append( new Json( c, sInput ) ); + skipWs( c, sInput ); if( c == ']' ) + { + readChar( c, sInput ); break; + } else if( c == ',' ) + { + next("array"); continue; + } else { throw Bu::ExceptionBase( @@ -236,10 +444,7 @@ void Bu::Json::parseArray( char &c, Bu::Stream &sInput ) void Bu::Json::parseNumber( char &c, Bu::Stream &sInput ) { - while( c != '-' && c < '0' && c > '9' ) - { - next( "number" ); - } + skipWs( c, sInput ); Bu::String sBuf; if( c == '-' ) @@ -274,10 +479,7 @@ void Bu::Json::parseNumber( char &c, Bu::Stream &sInput ) void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput ) { - while( c != 'f' && c != 't' && c != 'n' ) - { - next( "literal" ); - } + skipWs( c, sInput ); Bu::String s; do @@ -329,3 +531,57 @@ bool Bu::Json::isWs( char c ) return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } +void Bu::Json::skipWs( char &c, Bu::Stream &sInput ) +{ + while( isWs( c ) ) + { + next("whitespace"); + } +} + +void Bu::Json::writeStr( const Bu::String &sStr, Bu::Stream &sOutput ) const +{ + sOutput.write("\"", 1 ); + for( Bu::String::const_iterator i = sStr.begin(); i; i++ ) + { + switch( *i ) + { + case '"': + sOutput.write("\\\"", 2 ); + break; + + case '\\': + sOutput.write("\\\\", 2 ); + break; + + case '/': + sOutput.write("\\/", 2 ); + break; + + case '\b': + sOutput.write("\\b", 2 ); + break; + + case '\f': + sOutput.write("\\f", 2 ); + break; + + case '\n': + sOutput.write("\\n", 2 ); + break; + + case '\r': + sOutput.write("\\r", 2 ); + break; + + case '\t': + sOutput.write("\\t", 2 ); + break; + + default: + sOutput.write( &(*i), 1 ); + } + } + sOutput.write("\"", 1 ); +} + -- cgit v1.2.3