Repository: nbsdx/SimpleJSON Branch: master Commit: 8dd3e9b84a0b Files: 26 Total size: 36.9 KB Directory structure: gitextract_ue6jn_ul/ ├── .gitignore ├── API.md ├── README.md ├── build.sh ├── examples/ │ ├── array_example.cpp │ ├── init_example.cpp │ ├── iter_example.cpp │ ├── json_example.cpp │ ├── load_example.cpp │ └── prim_example.cpp ├── json.hpp └── test/ ├── cases/ │ ├── test1.json │ ├── test10.json │ ├── test11.json │ ├── test12.json │ ├── test13.json │ ├── test2.json │ ├── test3.json │ ├── test4.json │ ├── test5.json │ ├── test6.json │ ├── test7.json │ ├── test8.json │ └── test9.json ├── run.py └── tester.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # build.sh result */bin ================================================ FILE: API.md ================================================ # SimpleJSON API ## Overview ```cpp namespace json { /// Create a new JSON Array. JSON Array( [any_type [, ... ] ] ); /// Create a new JSON Object. JSON Object(); /// JSON Class. This is the core class. class JSON { enum Class { Null, Object, Array, String, Boolean, Floating, Integral }; /** Typed Constructors string_type: [const] char *, [const] char[], std::string, etc bool_type: bool numeric_type: char, int, long, double, float, etc null_type: nullptr_t */ JSON( string_type ); JSON( bool_type ); JSON( numeric_type ); JSON( null_type ); /** Copy/Move Constructors */ JSON( const JSON & ); JSON( JSON && ); /** Static Methods */ /// Create a JSON object from a string. JSON Load( string_type ); /// Create a JSON object with the specified json::Class type. JSON Make( JSON::Class ); /** Operator Overloading Assigning to a JSON object changes the type on the fly. If you have a JSON object that represents an integer, and then you assign a boolean value to it, that object now represents a boolean. */ /// Assign a boolean type to a JSON object JSON& operator=( bool_type ); /// Assign a numeric type to a JSON object JSON& operator=( numeric_type ); /// Assign a string type to a JSON object JSON& operator=( string_type ); /// Assign a null type to a JSON object // JSON& operator=( null_type ); // TODO: Not Impld /// Standard copy/move assignment operators JSON& operator=( const JSON & ); JSON& operator=( JSON && ); /// Access the elements of a JSON Object. /// Accessing an invalid key will create a new entry with a Null type. JSON& operator[]( string_type key ); /// Access the elements of a JSON Array. /// Accessing an out of bounds index will extend the Array. JSON& operator[]( unsigned index ); /// Same as operator[] JSON& at( string_type | unsigned ) /// const version of 'at' const JSON& at( string_type | unsigned ) const; /// Stream operator; calls dump() std::ostream& operator<<( std::ostream &, const JSON & ); /** Utility Methods */ /// Get the length of an array, or -1 int length() const; /// Get the size of an Array or Object int size() const; /// Determine if an Object has a key bool hasKey( string_type ) const; /// Useful for appending to an Array, can take any number of /// primitive types using variadic templates void append( any_type [, ... ] ); /// Dumps the JSON object to a string format for storing. void dump( int depth = 0, string indent = " " ); /// Get the JSON::Class type for a JSON object. JSON::Class JSONType(); /// Convience method to determine if an object is Class::Null bool IsNull(); /// Convert to a string literal iff Type == Class::String string ToString(); string ToString( bool &OK ); /// Convert to a floating literal iff Type == Class::Floating double ToFloat(); double ToFloat( bool &OK ); /// Convert to an integral literal iff Type == Class::Integral long ToInt(); long ToInt( bool &OK ); /// Convert to a boolean literal iff Type == Class::Boolean bool ToBool(); bool ToBool( bool &OK ); /** Iterating */ /// Wraps the internal object representation to access iterators. /// Will return empty range for non-object objects. JSONWrapper ObjectRange(); /// Wraps the internal array representation to access iterators. /// Will return empty range for non-array objects. JSONWrapper ArrayRange(); }; // End json::JSON documentation } // End json documentation ================================================ FILE: README.md ================================================ # SimpleJSON Simple C++ JSON library ## License Do what the fuck you want public license ## About SimpleJSON is a lightweight JSON library for exporting data in JSON format from C++. By taking advantage of templates and operator overloading on the backend, you're able to create and work with JSON objects right away, just as you would expect from a language such as JavaScript. SimpleJSON is a single C++ Header file, "json.hpp". Feel free to download this file on its own, and include it in your project. No other requirements! #### Platforms SimpleJSON should work on any platform; it's only requirement is a C++11 compatible compiler, as it make heavy use of the C++11 move semantics, and variadic templates. The tests are tailored for linux, but could be ported to any platform with python support and a C++11 compiler. ## API You can find the API [over here](API.md). For now it's just a Markdown file with C++ syntax highlighting, but it's better than nothing! ## Upcoming Features SimpleJSON is still missing some features, which I hope to get done soon! * Write more test cases to cover all major components( mostly parsing ) One of the biggests goals for SimpleJSON is for it to be lightweight, and small. Having complicated logic isn't bad, but it bloats the codebase in most cases. I'd like to keep things small rather than put in big features that take a ton of space. If you run into any bugs, or see that I'm missing a featuer, please submit an issue through GitHub and I'll respond as soon as I can! ## Example More examples can be found in the 'examples' directory. Check out [the API](API.md) for a full list of functions. ```cpp #include "json.hpp" int main() { json::JSON obj; // Create a new Array as a field of an Object. obj["array"] = json::Array( true, "Two", 3, 4.0 ); // Create a new Object as a field of another Object. obj["obj"] = json::Object(); // Assign to one of the inner object's fields obj["obj"]["inner"] = "Inside"; // We don't need to specify the type of the JSON object: obj["new"]["some"]["deep"]["key"] = "Value"; obj["array2"].append( false, "three" ); // We can also parse a string into a JSON object: obj["parsed"] = JSON::Load( "[ { \"Key\" : \"Value\" }, false ]" ); std::cout << obj << std::endl; } ``` Output: ``` { "array" : [true, "Two", 3, 4.000000], "array2" : [false, "three"], "new" : { "some" : { "deep" : { "key" : "Value" } } }, "obj" : { "inner" : "Inside" }, "parsed" : [{ "Key" : "Value" }, false] } ``` This example can also be written another way: ```cpp #include "json.hpp" #include using json::JSON; int main() { JSON obj = { "array", json::Array( true, "Two", 3, 4.0 ), "obj", { "inner", "Inside" }, "new", { "some", { "deep", { "key", "Value" } } }, "array2", json::Array( false, "three" ) }; std::cout << obj << std::endl; ``` Sadly, we don't have access to the : character in C++, so we can't use that to seperate key-value pairs, but by using commas, we can achieve a very similar effect. The other point you might notice, is that we have to explictly create arrays. This is a limitation of C++'s operator overloading rules, so we can't use the [] operator to define the array :( I'm looking into ways to make this smoother. ================================================ FILE: build.sh ================================================ #!/bin/sh mkdir -p examples/bin mkdir -p test/bin # Build Examples. clang++ -std=c++11 -I. ./examples/json_example.cpp -o ./examples/bin/json_example clang++ -std=c++11 -I. ./examples/array_example.cpp -o ./examples/bin/array_example clang++ -std=c++11 -I. ./examples/prim_example.cpp -o ./examples/bin/prim_example clang++ -std=c++11 -I. ./examples/init_example.cpp -o ./examples/bin/init_example clang++ -std=c++11 -I. ./examples/load_example.cpp -o ./examples/bin/load_example clang++ -std=c++11 -I. ./examples/iter_example.cpp -o ./examples/bin/iter_example # Build Test Tool clang++ -std=c++11 -I. ./test/tester.cpp -o ./test/bin/tester echo "Done. See './examples' for examples, and './examples/bin' for the executables." echo "To run tests, run cd test; python ./run.py" ================================================ FILE: examples/array_example.cpp ================================================ #include "json.hpp" #include using json::JSON; using namespace std; int main() { JSON array; array[2] = "Test2"; cout << array << endl; array[1] = "Test1"; cout << array << endl; array[0] = "Test0"; cout << array << endl; array[3] = "Test4"; cout << array << endl; // Arrays can be nested: JSON Array2; Array2[2][0][1] = true; cout << Array2 << endl; } ================================================ FILE: examples/init_example.cpp ================================================ #include "json.hpp" #include #include using json::JSON; using namespace std; int main() { JSON obj( { "Key", 1, "Key3", true, "Key4", nullptr, "Key2", { "Key4", "VALUE", "Arr", json::Array( 1, "Str", false ) } } ); cout << obj << endl; } ================================================ FILE: examples/iter_example.cpp ================================================ #include "json.hpp" #include using json::JSON; using namespace std; void dumpArrayConst( const JSON &array ) { for( auto &j : array.ArrayRange() ) std::cout << "Value: " << j << "\n"; } void dumpArray( JSON &array ) { for( auto &j : array.ArrayRange() ) std::cout << "Value: " << j << "\n"; } void dumpObjectConst( const JSON &object ) { for( auto &j : object.ObjectRange() ) std::cout << "Object[ " << j.first << " ] = " << j.second << "\n"; } void dumpObject( JSON &object ) { for( auto &j : object.ObjectRange() ) std::cout << "Object[ " << j.first << " ] = " << j.second << "\n"; } int main() { JSON array = JSON::Make( JSON::Class::Array ); JSON obj = JSON::Make( JSON::Class::Object ); array[0] = "Test0"; array[1] = "Test1"; array[2] = "Test2"; array[3] = "Test3"; obj[ "Key0" ] = "Value1"; obj[ "Key1" ] = array; obj[ "Key2" ] = 123; std::cout << "=============== tests ================\n"; dumpArray( array ); dumpObject( obj ); std::cout << "============ const tests =============\n"; dumpArrayConst( array ); dumpObjectConst( obj ); } ================================================ FILE: examples/json_example.cpp ================================================ #include "json.hpp" #include using json::JSON; using namespace std; int main() { // Example of creating each type // You can also do JSON::Make( JSON::Class ) JSON null; JSON Bool( true ); JSON Str( "RawString" ); JSON Str2( string( "C++String" ) ); JSON Int( 1 ); JSON Float( 1.2 ); JSON Arr = json::Array(); JSON Obj = json::Object(); // Types can be overwritten by assigning // to the object again. Bool = false; Bool = "rtew"; Bool = 1; Bool = 1.1; Bool = string( "asd" ); // Append to Arrays, appending to a non-array // will turn the object into an array with the // first element being the value that's being // appended. Arr.append( 1 ); Arr.append( "test" ); Arr.append( false ); // Access Array elements with operator[]( unsigned ). // Note that this does not do bounds checking, and // returns a reference to a JSON object. JSON& val = Arr[0]; // Arrays can be intialized with any elements and // they are turned into JSON objects. Variadic // Templates are pretty cool. JSON Arr2 = json::Array( 2, "Test", true ); // Objects are accessed using operator[]( string ). // Will create new pairs on the fly, just as std::map // would. Obj["Key1"] = 1.0; Obj["Key2"] = "Value"; JSON Obj2 = json::Object(); Obj2["Key3"] = 1; Obj2["Key4"] = Arr; Obj2["Key5"] = Arr2; // Nested Object Obj["Key6"] = Obj2; // Dump Obj to a string. cout << Obj << endl; // We can also use a more JSON-like syntax to create // JSON Objects. JSON Obj3 = { "Key1", "Value", "Key2", true, "Key3", { "Key4", json::Array( "This", "Is", "An", "Array" ), "Key5", { "BooleanValue", true } } }; cout << Obj3 << endl; } ================================================ FILE: examples/load_example.cpp ================================================ #include "json.hpp" #include using namespace std; using json::JSON; int main() { JSON Int = JSON::Load( " 123 " ); JSON Float = JSON::Load( " 123.234 " ); JSON Str = JSON::Load( "\"String\"" ); JSON EscStr = JSON::Load( "\" \\\"Some\\/thing\\\" \"" ); JSON Arr = JSON::Load( "[1,2, true, false,\"STRING\", 1.5]" ); JSON Obj = JSON::Load( "{ \"Key\" : \"StringValue\"," " \"Key2\" : true, " " \"Key3\" : 1234, " " \"Key4\" : null }" ); cout << Int << endl; cout << Float << endl; cout << Str << endl; cout << EscStr << endl; cout << Arr << endl; cout << Obj << endl; } ================================================ FILE: examples/prim_example.cpp ================================================ #include "json.hpp" #include #include using json::JSON; using namespace std; int main() { JSON obj; obj = true; cout << "Value: " << boolalpha << obj.ToBool() << endl; obj = "Test String"; cout << "Value: " << obj.ToString() << endl; obj = 2.2; cout << "Value: " << obj.ToFloat() << endl; obj = 3; cout << "Value: " << obj.ToInt() << endl; } ================================================ FILE: json.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace json { using std::map; using std::deque; using std::string; using std::enable_if; using std::initializer_list; using std::is_same; using std::is_convertible; using std::is_integral; using std::is_floating_point; namespace { string json_escape( const string &str ) { string output; for( unsigned i = 0; i < str.length(); ++i ) switch( str[i] ) { case '\"': output += "\\\""; break; case '\\': output += "\\\\"; break; case '\b': output += "\\b"; break; case '\f': output += "\\f"; break; case '\n': output += "\\n"; break; case '\r': output += "\\r"; break; case '\t': output += "\\t"; break; default : output += str[i]; break; } return std::move( output ); } } class JSON { union BackingData { BackingData( double d ) : Float( d ){} BackingData( long l ) : Int( l ){} BackingData( bool b ) : Bool( b ){} BackingData( string s ) : String( new string( s ) ){} BackingData() : Int( 0 ){} deque *List; map *Map; string *String; double Float; long Int; bool Bool; } Internal; public: enum class Class { Null, Object, Array, String, Floating, Integral, Boolean }; template class JSONWrapper { Container *object; public: JSONWrapper( Container *val ) : object( val ) {} JSONWrapper( std::nullptr_t ) : object( nullptr ) {} typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } }; template class JSONConstWrapper { const Container *object; public: JSONConstWrapper( const Container *val ) : object( val ) {} JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } }; JSON() : Internal(), Type( Class::Null ){} JSON( initializer_list list ) : JSON() { SetType( Class::Object ); for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) operator[]( i->ToString() ) = *std::next( i ); } JSON( JSON&& other ) : Internal( other.Internal ) , Type( other.Type ) { other.Type = Class::Null; other.Internal.Map = nullptr; } JSON& operator=( JSON&& other ) { ClearInternal(); Internal = other.Internal; Type = other.Type; other.Internal.Map = nullptr; other.Type = Class::Null; return *this; } JSON( const JSON &other ) { switch( other.Type ) { case Class::Object: Internal.Map = new map( other.Internal.Map->begin(), other.Internal.Map->end() ); break; case Class::Array: Internal.List = new deque( other.Internal.List->begin(), other.Internal.List->end() ); break; case Class::String: Internal.String = new string( *other.Internal.String ); break; default: Internal = other.Internal; } Type = other.Type; } JSON& operator=( const JSON &other ) { ClearInternal(); switch( other.Type ) { case Class::Object: Internal.Map = new map( other.Internal.Map->begin(), other.Internal.Map->end() ); break; case Class::Array: Internal.List = new deque( other.Internal.List->begin(), other.Internal.List->end() ); break; case Class::String: Internal.String = new string( *other.Internal.String ); break; default: Internal = other.Internal; } Type = other.Type; return *this; } ~JSON() { switch( Type ) { case Class::Array: delete Internal.List; break; case Class::Object: delete Internal.Map; break; case Class::String: delete Internal.String; break; default:; } } template JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} template JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){} template JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){} template JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} static JSON Make( Class type ) { JSON ret; ret.SetType( type ); return ret; } static JSON Load( const string & ); template void append( T arg ) { SetType( Class::Array ); Internal.List->emplace_back( arg ); } template void append( T arg, U... args ) { append( arg ); append( args... ); } template typename enable_if::value, JSON&>::type operator=( T b ) { SetType( Class::Boolean ); Internal.Bool = b; return *this; } template typename enable_if::value && !is_same::value, JSON&>::type operator=( T i ) { SetType( Class::Integral ); Internal.Int = i; return *this; } template typename enable_if::value, JSON&>::type operator=( T f ) { SetType( Class::Floating ); Internal.Float = f; return *this; } template typename enable_if::value, JSON&>::type operator=( T s ) { SetType( Class::String ); *Internal.String = string( s ); return *this; } JSON& operator[]( const string &key ) { SetType( Class::Object ); return Internal.Map->operator[]( key ); } JSON& operator[]( unsigned index ) { SetType( Class::Array ); if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); return Internal.List->operator[]( index ); } JSON &at( const string &key ) { return operator[]( key ); } const JSON &at( const string &key ) const { return Internal.Map->at( key ); } JSON &at( unsigned index ) { return operator[]( index ); } const JSON &at( unsigned index ) const { return Internal.List->at( index ); } int length() const { if( Type == Class::Array ) return Internal.List->size(); else return -1; } bool hasKey( const string &key ) const { if( Type == Class::Object ) return Internal.Map->find( key ) != Internal.Map->end(); return false; } int size() const { if( Type == Class::Object ) return Internal.Map->size(); else if( Type == Class::Array ) return Internal.List->size(); else return -1; } Class JSONType() const { return Type; } /// Functions for getting primitives from the JSON object. bool IsNull() const { return Type == Class::Null; } string ToString() const { bool b; return std::move( ToString( b ) ); } string ToString( bool &ok ) const { ok = (Type == Class::String); return ok ? std::move( json_escape( *Internal.String ) ): string(""); } double ToFloat() const { bool b; return ToFloat( b ); } double ToFloat( bool &ok ) const { ok = (Type == Class::Floating); return ok ? Internal.Float : 0.0; } long ToInt() const { bool b; return ToInt( b ); } long ToInt( bool &ok ) const { ok = (Type == Class::Integral); return ok ? Internal.Int : 0; } bool ToBool() const { bool b; return ToBool( b ); } bool ToBool( bool &ok ) const { ok = (Type == Class::Boolean); return ok ? Internal.Bool : false; } JSONWrapper> ObjectRange() { if( Type == Class::Object ) return JSONWrapper>( Internal.Map ); return JSONWrapper>( nullptr ); } JSONWrapper> ArrayRange() { if( Type == Class::Array ) return JSONWrapper>( Internal.List ); return JSONWrapper>( nullptr ); } JSONConstWrapper> ObjectRange() const { if( Type == Class::Object ) return JSONConstWrapper>( Internal.Map ); return JSONConstWrapper>( nullptr ); } JSONConstWrapper> ArrayRange() const { if( Type == Class::Array ) return JSONConstWrapper>( Internal.List ); return JSONConstWrapper>( nullptr ); } string dump( int depth = 1, string tab = " ") const { string pad = ""; for( int i = 0; i < depth; ++i, pad += tab ); switch( Type ) { case Class::Null: return "null"; case Class::Object: { string s = "{\n"; bool skip = true; for( auto &p : *Internal.Map ) { if( !skip ) s += ",\n"; s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); skip = false; } s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; return s; } case Class::Array: { string s = "["; bool skip = true; for( auto &p : *Internal.List ) { if( !skip ) s += ", "; s += p.dump( depth + 1, tab ); skip = false; } s += "]"; return s; } case Class::String: return "\"" + json_escape( *Internal.String ) + "\""; case Class::Floating: return std::to_string( Internal.Float ); case Class::Integral: return std::to_string( Internal.Int ); case Class::Boolean: return Internal.Bool ? "true" : "false"; default: return ""; } return ""; } friend std::ostream& operator<<( std::ostream&, const JSON & ); private: void SetType( Class type ) { if( type == Type ) return; ClearInternal(); switch( type ) { case Class::Null: Internal.Map = nullptr; break; case Class::Object: Internal.Map = new map(); break; case Class::Array: Internal.List = new deque(); break; case Class::String: Internal.String = new string(); break; case Class::Floating: Internal.Float = 0.0; break; case Class::Integral: Internal.Int = 0; break; case Class::Boolean: Internal.Bool = false; break; } Type = type; } private: /* beware: only call if YOU know that Internal is allocated. No checks performed here. This function should be called in a constructed JSON just before you are going to overwrite Internal... */ void ClearInternal() { switch( Type ) { case Class::Object: delete Internal.Map; break; case Class::Array: delete Internal.List; break; case Class::String: delete Internal.String; break; default:; } } private: Class Type = Class::Null; }; JSON Array() { return std::move( JSON::Make( JSON::Class::Array ) ); } template JSON Array( T... args ) { JSON arr = JSON::Make( JSON::Class::Array ); arr.append( args... ); return std::move( arr ); } JSON Object() { return std::move( JSON::Make( JSON::Class::Object ) ); } std::ostream& operator<<( std::ostream &os, const JSON &json ) { os << json.dump(); return os; } namespace { JSON parse_next( const string &, size_t & ); void consume_ws( const string &str, size_t &offset ) { while( isspace( str[offset] ) ) ++offset; } JSON parse_object( const string &str, size_t &offset ) { JSON Object = JSON::Make( JSON::Class::Object ); ++offset; consume_ws( str, offset ); if( str[offset] == '}' ) { ++offset; return std::move( Object ); } while( true ) { JSON Key = parse_next( str, offset ); consume_ws( str, offset ); if( str[offset] != ':' ) { std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n"; break; } consume_ws( str, ++offset ); JSON Value = parse_next( str, offset ); Object[Key.ToString()] = Value; consume_ws( str, offset ); if( str[offset] == ',' ) { ++offset; continue; } else if( str[offset] == '}' ) { ++offset; break; } else { std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n"; break; } } return std::move( Object ); } JSON parse_array( const string &str, size_t &offset ) { JSON Array = JSON::Make( JSON::Class::Array ); unsigned index = 0; ++offset; consume_ws( str, offset ); if( str[offset] == ']' ) { ++offset; return std::move( Array ); } while( true ) { Array[index++] = parse_next( str, offset ); consume_ws( str, offset ); if( str[offset] == ',' ) { ++offset; continue; } else if( str[offset] == ']' ) { ++offset; break; } else { std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n"; return std::move( JSON::Make( JSON::Class::Array ) ); } } return std::move( Array ); } JSON parse_string( const string &str, size_t &offset ) { JSON String; string val; for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) { if( c == '\\' ) { switch( str[ ++offset ] ) { case '\"': val += '\"'; break; case '\\': val += '\\'; break; case '/' : val += '/' ; break; case 'b' : val += '\b'; break; case 'f' : val += '\f'; break; case 'n' : val += '\n'; break; case 'r' : val += '\r'; break; case 't' : val += '\t'; break; case 'u' : { val += "\\u" ; for( unsigned i = 1; i <= 4; ++i ) { c = str[offset+i]; if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) val += c; else { std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n"; return std::move( JSON::Make( JSON::Class::String ) ); } } offset += 4; } break; default : val += '\\'; break; } } else val += c; } ++offset; String = val; return std::move( String ); } JSON parse_number( const string &str, size_t &offset ) { JSON Number; string val, exp_str; char c; bool isDouble = false; long exp = 0; while( true ) { c = str[offset++]; if( (c == '-') || (c >= '0' && c <= '9') ) val += c; else if( c == '.' ) { val += c; isDouble = true; } else break; } if( c == 'E' || c == 'e' ) { c = str[ offset++ ]; if( c == '-' ){ ++offset; exp_str += '-';} while( true ) { c = str[ offset++ ]; if( c >= '0' && c <= '9' ) exp_str += c; else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n"; return std::move( JSON::Make( JSON::Class::Null ) ); } else break; } exp = std::stol( exp_str ); } else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; return std::move( JSON::Make( JSON::Class::Null ) ); } --offset; if( isDouble ) Number = std::stod( val ) * std::pow( 10, exp ); else { if( !exp_str.empty() ) Number = std::stol( val ) * std::pow( 10, exp ); else Number = std::stol( val ); } return std::move( Number ); } JSON parse_bool( const string &str, size_t &offset ) { JSON Bool; if( str.substr( offset, 4 ) == "true" ) Bool = true; else if( str.substr( offset, 5 ) == "false" ) Bool = false; else { std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n"; return std::move( JSON::Make( JSON::Class::Null ) ); } offset += (Bool.ToBool() ? 4 : 5); return std::move( Bool ); } JSON parse_null( const string &str, size_t &offset ) { JSON Null; if( str.substr( offset, 4 ) != "null" ) { std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n"; return std::move( JSON::Make( JSON::Class::Null ) ); } offset += 4; return std::move( Null ); } JSON parse_next( const string &str, size_t &offset ) { char value; consume_ws( str, offset ); value = str[offset]; switch( value ) { case '[' : return std::move( parse_array( str, offset ) ); case '{' : return std::move( parse_object( str, offset ) ); case '\"': return std::move( parse_string( str, offset ) ); case 't' : case 'f' : return std::move( parse_bool( str, offset ) ); case 'n' : return std::move( parse_null( str, offset ) ); default : if( ( value <= '9' && value >= '0' ) || value == '-' ) return std::move( parse_number( str, offset ) ); } std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n"; return JSON(); } } JSON JSON::Load( const string &str ) { size_t offset = 0; return std::move( parse_next( str, offset ) ); } } // End Namespace json ================================================ FILE: test/cases/test1.json ================================================ null ================================================ FILE: test/cases/test10.json ================================================ "This is a\n\nMultiline string" ================================================ FILE: test/cases/test11.json ================================================ { "T1" : "Value With a Quote : \"", "T2" : "Value With a Rev Solidus : \/", "T3" : "Value with a Solidus : \\", "T4" : "Value with a Backspace : \b", "T5" : "Value with a Formfeed : \f", "T6" : "Value with a Newline : \n", "T7" : "Value with a Carriage Return : \r", "T8" : "Value with a Horizontal Tab : \t" } ================================================ FILE: test/cases/test12.json ================================================ "" ================================================ FILE: test/cases/test13.json ================================================ 1.20E+2 ================================================ FILE: test/cases/test2.json ================================================ true ================================================ FILE: test/cases/test3.json ================================================ 100 ================================================ FILE: test/cases/test4.json ================================================ 1.234 ================================================ FILE: test/cases/test5.json ================================================ "StringTest" ================================================ FILE: test/cases/test6.json ================================================ {} ================================================ FILE: test/cases/test7.json ================================================ { "Key" : "Value" } ================================================ FILE: test/cases/test8.json ================================================ [] ================================================ FILE: test/cases/test9.json ================================================ [1,2,3] ================================================ FILE: test/run.py ================================================ #!/usr/bin/python import json from os import listdir from os.path import isfile, join from subprocess import Popen, PIPE inputs = sorted( [ './cases/' + f for f in listdir( './cases' ) if isfile( join( './cases', f ) ) ] ) for x in inputs: try: p = Popen(['./bin/tester', x], stdout=PIPE) output, _ = p.communicate() rc = p.returncode expected = json.loads( open( x ).read() ) actual = json.loads( output ) if not (expected == actual): print 'Error: Failed', x print 'Expected:', expected print 'Actual:', actual else: print x,'passed.' except: print 'Error: Failed', x, '- Subprocess failed' ================================================ FILE: test/tester.cpp ================================================ #include "json.hpp" #include #include #include using namespace std; using json::JSON; void usage( char *name ) { std::cout << "Usage: " << name << " input.json output.json\n"; exit( 1 ); } /** * Simple testing program that takes an input * JSON file, loads it, and then dumps it the * given output file. */ int main( int argc, char **argv ) { if( argc != 2 ) usage( argv[0] ); string contents; ifstream input( argv[1] ); input.seekg( 0, ios::end ); contents.reserve( input.tellg() ); input.seekg( 0, ios::beg ); contents.assign( (istreambuf_iterator( input )), istreambuf_iterator() ); JSON obj = JSON::Load( contents ); //ofstream output( argv[2] ); cout << obj << endl; }