From b7751f0136502592e9b1897b51859b1133339a93 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 6 Jun 2017 12:30:52 -0700 Subject: Hey! This is a much better structure for the Json class. This new setup is one class for everything, the values are kept in a union, and the instance knows what type it is. The tree of objects is a tree of instantiations of the same class. It's much simpler, it's much easier to write, and maybe even easier to use. --- src/unstable/json.cpp | 316 ++++++++++++++++++++++++++++++++++++++++++++------ src/unstable/json.h | 113 +++++++----------- 2 files changed, 323 insertions(+), 106 deletions(-) (limited to 'src/unstable') diff --git a/src/unstable/json.cpp b/src/unstable/json.cpp index 0769836..c31c7f9 100644 --- a/src/unstable/json.cpp +++ b/src/unstable/json.cpp @@ -1,87 +1,331 @@ #include "bu/json.h" +#include "bu/staticmembuf.h" -Bu::Json::Base::Base() -{ -} +#include -Bu::Json::Base::~Base() -{ -} +#define next( txt ) readChar( c, sInput, "Unexpected end of stream while reading " txt "." ) -Bu::Json::Object::Object() +Bu::Json::Json() : + eType( Invalid ) { } -Bu::Json::Object::~Object() +Bu::Json::Json( const Bu::String &sJson ) : + eType( Invalid ) { + Bu::StaticMemBuf mIn( sJson.getStr(), sJson.getSize() ); + parse( mIn ); } -Bu::Json::Type Bu::Json::Object::getType() +Bu::Json::Json( Bu::Stream &sInput ) : + eType( Invalid ) { - return tObject; + parse( sInput ); } -Bu::Json::Array::Array() +Bu::Json::~Json() { + reset(); } -Bu::Json::Array::~Array() +void Bu::Json::parse( Bu::Stream &sInput ) { -} + reset(); -Bu::Json::Type Bu::Json::Array::getType() -{ - return tArray; + 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 ); + } + } } -Bu::Json::String::String() +void Bu::Json::reset() { -} + switch( eType ) + { + case Object: + for( JsonHash::iterator i = uDat.pObject->begin(); i; i++ ) + { + delete i.getValue(); + } + delete uDat.pObject; + break; -Bu::Json::String::~String() -{ -} + case Array: + for( JsonList::iterator i = uDat.pArray->begin(); i; i++ ) + { + delete *i; + } + delete uDat.pArray; + break; -Bu::Json::Type Bu::Json::String::getType() -{ - return tString; + case String: + delete uDat.pString; + break; + + case Invalid: + case Number: + case Boolean: + case Null: + break; + } + uDat.pObject = NULL; } -Bu::Json::Number::Number() +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; + } + } } -Bu::Json::Number::~Number() +void Bu::Json::parseString( char &c, Bu::Stream &sInput ) { + eType = String; + uDat.pString = new Bu::String(); + parseString( c, sInput, *uDat.pString ); } -Bu::Json::Type Bu::Json::Number::getType() +void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) { - return tNumber; + 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." + ); + } } -Bu::Json::Boolean::Boolean() +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." + ); + } + } } -Bu::Json::Boolean::~Boolean() +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() ); } -Bu::Json::Type Bu::Json::Boolean::getType() +void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput ) { - return tBoolean; + 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."); + } } -Bu::Json::Null::Null() +bool Bu::Json::readChar( char &c, Bu::Stream &sInput ) { + if( sInput.read( &c, 1 ) == 0 ) + return false; + return true; } -Bu::Json::Null::~Null() +void Bu::Json::readChar( char &c, Bu::Stream &sInput, const char *sSection ) { + if( sInput.read( &c, 1 ) == 0 ) + { + throw Bu::ExceptionBase( sSection ); + } } -Bu::Json::Type Bu::Json::Null::getType() +bool Bu::Json::isWs( char c ) { - return tNull; + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } + diff --git a/src/unstable/json.h b/src/unstable/json.h index 660d1c6..47009cb 100644 --- a/src/unstable/json.h +++ b/src/unstable/json.h @@ -1,89 +1,62 @@ #ifndef BU_JSON_H #define BU_JSON_H +#include "bu/hash.h" +#include "bu/list.h" +#include "bu/string.h" + namespace Bu { + class Stream; + class Json { - public: - Json(); - virtual ~Json(); - public: enum Type { - tObject, - tArray, - tString, - tNumber, - tBoolean, - tNull - }; - - class Base - { - public: - Base(); - virtual ~Base(); - - virtual Type getType()=0; - }; - - class Object : public Base - { - public: - Object(); - virtual ~Object(); - - virtual Type getType(); - }; - - class Array : public Base - { - public: - Array(); - virtual ~Array(); - - virtual Type getType(); + Invalid, + Object, + Array, + String, + Number, + Boolean, + Null }; - class String : public Base - { - public: - String(); - virtual ~String(); - - virtual Type getType(); - }; - - class Number : public Base - { - public: - Number(); - virtual ~Number(); - - virtual Type getType(); - }; - - class Boolean : public Base - { - public: - Boolean(); - virtual ~Boolean(); - - virtual Type getType(); - }; + public: + Json(); + Json( const Bu::String &sJson ); + Json( Bu::Stream &sInput ); + virtual ~Json(); - class Null : public Base - { - public: - Null(); - virtual ~Null(); + void parse( Bu::Stream &sInput ); + void reset(); - virtual Type getType(); - }; + private: + void parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ); + void parseString( char &c, Bu::Stream &sInput ); + void parseObject( char &c, Bu::Stream &sInput ); + void parseArray( char &c, Bu::Stream &sInput ); + void parseNumber( char &c, Bu::Stream &sInput ); + void parseLiteral( char &c, Bu::Stream &sInput ); + bool readChar( char &c, Bu::Stream &sInput ); + void readChar( char &c, Bu::Stream &sInput, const char *sSection ); + bool isWs( char c ); private: + typedef Bu::Hash JsonHash; + typedef Bu::List JsonList; + + Type eType; + union DatUnion + { + DatUnion() : pObject( NULL ) { } + JsonHash *pObject; + JsonList *pArray; + Bu::String *pString; + double dNumber; + bool bBoolean; + } uDat; }; }; -- cgit v1.2.3