├── .gitmodules ├── UnitTest ├── checkJson.cpp ├── CMakeLists.txt └── test.cpp ├── CMakeLists.txt ├── Source ├── include │ ├── jsonException.h │ ├── parse.h │ ├── jsonValue.h │ └── json.h ├── CMakeLists.txt └── src │ ├── jsonValue.cpp │ ├── json.cpp │ └── parse.cpp ├── .gitignore ├── LICENSE └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gtest"] 2 | path = gtest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /UnitTest/checkJson.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "json.h" 3 | 4 | using namespace ::std; 5 | using namespace ::miniJson; 6 | 7 | int main(int argc, char* argv[]) { 8 | testing::InitGoogleTest(&argc, argv); 9 | RUN_ALL_TESTS(); 10 | return 0; 11 | } -------------------------------------------------------------------------------- /UnitTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | project (MiniJsonTest) 4 | 5 | # Use C++11 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | message(STATUS "C++17 support has been enabled by default.") 9 | 10 | FILE(GLOB_RECURSE TEST_SRC "${CMAKE_SOURCE_DIR}/UnitTest/*.cpp") 11 | 12 | add_executable(${PROJECT_NAME} ${TEST_SRC}) 13 | 14 | target_link_libraries(${PROJECT_NAME} gtest Json) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | project(MiniJson) 4 | message("current build type is ${CMAKE_BUILD_TYPE}") 5 | 6 | # Use C++17 7 | set(CMAKE_CXX_STANDARD 17) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | message(STATUS "C++17 support has been enabled by default.") 10 | 11 | add_subdirectory("Source") 12 | 13 | if (ENABLE_MINIJSON_TEST) 14 | add_subdirectory("gtest") 15 | add_subdirectory("UnitTest") 16 | endif() 17 | -------------------------------------------------------------------------------- /Source/include/jsonException.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace miniJson { 6 | 7 | class JsonException : public std::runtime_error { 8 | public: // ctor 9 | explicit JsonException(const std::string& errMsg) : runtime_error(errMsg) {} 10 | 11 | public: // interface 12 | // what():Indication of exception information 13 | const char* what() const noexcept override { return runtime_error::what(); } 14 | }; 15 | 16 | } // namespace miniJson 17 | -------------------------------------------------------------------------------- /Source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | project (Json) 4 | 5 | # Use C++11 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | message(STATUS "C++17 support has been enabled by default.") 9 | 10 | file(GLOB_RECURSE SOURCE_FILES *.cpp *.h) 11 | 12 | add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) 13 | target_include_directories(${PROJECT_NAME} PUBLIC 14 | "${PROJECT_SOURCE_DIR}/include" 15 | "${PROJECT_SOURCE_DIR}/src") 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Jetbrain 2 | .idea/ 3 | cmake-build-debug/ 4 | 5 | # MacOS DS_Store 6 | .DS_Store 7 | 8 | # GoogleTest 9 | /gtest/ 10 | 11 | # Prerequisites 12 | *.d 13 | 14 | # Compiled Object files 15 | *.slo 16 | *.lo 17 | *.o 18 | *.obj 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Compiled Dynamic libraries 25 | *.so 26 | *.dylib 27 | *.dll 28 | 29 | # Fortran module files 30 | *.mod 31 | *.smod 32 | 33 | # Compiled Static libraries 34 | *.lai 35 | *.la 36 | *.a 37 | *.lib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | 44 | # tool scripts 45 | .clang-format 46 | codeFormat.sh 47 | cppLint.sh 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ZSMJ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/include/parse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "json.h" 4 | #include "jsonException.h" 5 | 6 | namespace miniJson { 7 | 8 | // constexpr function to determine a char is a specified number 9 | constexpr bool is1to9(char ch) { return ch >= '1' && ch <= '9'; } 10 | constexpr bool is0to9(char ch) { return ch >= '0' && ch <= '9'; } 11 | 12 | class Parser { 13 | public: // ctor 14 | explicit Parser(const char* cstr) noexcept : _start(cstr), _curr(cstr) {} 15 | explicit Parser(const std::string& content) noexcept 16 | : _start(content.c_str()), 17 | _curr(content.c_str()) {} 18 | 19 | public: // uncopyable 20 | Parser(const Parser&) = delete; 21 | Parser& operator=(const Parser&) = delete; 22 | 23 | private: // parse_aux interface 24 | void parseWhitespace() noexcept; 25 | unsigned parse4hex(); 26 | std::string encodeUTF8(unsigned u) noexcept; 27 | std::string parseRawString(); 28 | // indicate the error pos 29 | void error(const std::string& msg) const; 30 | 31 | private: // parse interface 32 | Json parseValue(); 33 | Json parseLiteral(const std::string& literal); 34 | Json parseNumber(); 35 | Json parseString(); 36 | Json parseArray(); 37 | Json parseObject(); 38 | 39 | public: // only public parse interface 40 | Json parse(); 41 | 42 | private: // private data member 43 | // two const char* points to the valid context's cur pos and start pos 44 | const char* _start; 45 | const char* _curr; 46 | }; 47 | } -------------------------------------------------------------------------------- /Source/include/jsonValue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "json.h" 5 | #include "jsonException.h" 6 | 7 | namespace miniJson { 8 | 9 | // Implement class:JsonValue 10 | class JsonValue { 11 | public: // ctor 12 | explicit JsonValue(std::nullptr_t) : _val(nullptr) {} 13 | explicit JsonValue(bool val) : _val(val) {} 14 | explicit JsonValue(double val) : _val(val) {} 15 | explicit JsonValue(const std::string& val) : _val(val) {} 16 | explicit JsonValue(const Json::_array& val) : _val(val) {} 17 | explicit JsonValue(const Json::_object& val) : _val(val) {} 18 | 19 | public: // move ctor for string,array and object 20 | explicit JsonValue(std::string&& val) : _val(std::move(val)) {} 21 | explicit JsonValue(Json::_array&& val) : _val(std::move(val)) {} 22 | explicit JsonValue(Json::_object&& val) : _val(std::move(val)) {} 23 | 24 | public: // dtor 25 | ~JsonValue() = default; 26 | 27 | public: // type interface 28 | JsonType getType() const noexcept; 29 | 30 | public: // interface for array and object 31 | size_t size() const; 32 | // random_access 33 | const Json& operator[](size_t) const; 34 | Json& operator[](size_t); 35 | // O(1) search(not insert) 36 | const Json& operator[](const std::string&) const; 37 | Json& operator[](const std::string&); 38 | 39 | public: // convert jsonValue into value instance 40 | std::nullptr_t toNull() const; 41 | bool toBool() const; 42 | double toDouble() const; 43 | const std::string& toString() const; 44 | const Json::_array& toArray() const; 45 | const Json::_object& toObject() const; 46 | 47 | private: 48 | // std::varient is a C++17 features,like union in C language 49 | // More information can be seen in 50 | // https://en.cppreference.com/w/cpp/utility/variant 51 | std::variant 53 | _val; 54 | }; 55 | 56 | } // namespace miniJson -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MiniJson 2 | ---------- 3 | [![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/) 4 | [![Standard](https://img.shields.io/badge/c%2B%2B-17-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) 5 | 6 | JSON is a lightweight data-interchange format. It can represent numbers, strings, ordered sequences of values, and collections of name/value pairs. 7 | 8 | MiniJson is a tiny JSON library that allows manipulating JSON values, including serialization and deserialization to and from strings. 9 | 10 | MiniJson is written in C++17 and test the code using the GoogleTest framework.The classes are heavily unit-tested and covers 100% of the code, including all exceptional behavior. 11 | 12 | 13 | ---------- 14 | The core object provided by the library is MiniJson::Json. A Json object represents any JSON value: null(nullptr_t), bool, number (int or double), string(std::string), array(std::vector), or object (std::unordered_map). 15 | 16 | Json objects act like values. They can be assigned, copied, moved, compared for equality and so on. There are also helper methods Json::serialize, to serialize a Json to a string, and Json::parse (static) to parse a std::string or const char* as a Json object. 17 | 18 | ---------- 19 | We can construct a JSON object very intuitively: 20 | ```C++ 21 | Json my_json1(nullptr); 22 | Json my_json2 = "string"; 23 | 24 | Json my_json3 = Json::_object { 25 | { "key1", "value1" }, 26 | { "key2", false }, 27 | { "key3", Json::_array { 1, 2, 3 } }, 28 | }; 29 | ``` 30 | String can be expressed explicitly using Json::parse() easily: 31 | ```C++ 32 | string errMsg;// store error messages when catch a exception in parse process 33 | auto js = Json::parse("[ null , false , true , 123 , \"abc\" ]",errmsg); 34 | cout << js[4] << endl; 35 | ``` 36 | You can also get a string representation of a JSON value (serialize): 37 | ```C++ 38 | auto str = js.serialize(); 39 | ``` 40 | Even you can try to output the JSON object directly: 41 | ```C++ 42 | cout << js << endl; 43 | ``` 44 | 45 | ---------- 46 | This work is inspired by https://github.com/miloyip/json-tutorial and https://github.com/Yuan-Hang/Json. 47 | 48 | More documentation is still to come. For now, please see Json.h. 49 | 50 | -------------------------------------------------------------------------------- /Source/src/jsonValue.cpp: -------------------------------------------------------------------------------- 1 | #include "jsonValue.h" 2 | #include "jsonException.h" 3 | 4 | namespace miniJson { 5 | 6 | // type interface 7 | JsonType JsonValue::getType() const noexcept { 8 | if (std::holds_alternative(_val)) { 9 | return JsonType::kNull; 10 | } else if (std::holds_alternative(_val)) { 11 | return JsonType::kBool; 12 | } else if (std::holds_alternative(_val)) { 13 | return JsonType::kNumber; 14 | } else if (std::holds_alternative(_val)) { 15 | return JsonType::kString; 16 | } else if (std::holds_alternative(_val)) { 17 | return JsonType::kArray; 18 | } else { 19 | return JsonType::kObject; 20 | } 21 | } 22 | 23 | // interface for array and object 24 | size_t JsonValue::size() const { 25 | if (std::holds_alternative(_val)) { 26 | return std::get(_val).size(); 27 | } else if (std::holds_alternative(_val)) { 28 | return std::get(_val).size(); 29 | } else { 30 | throw JsonException("not a array or object"); 31 | } 32 | } 33 | 34 | // operator[] for array:random_access 35 | const Json& JsonValue::operator[](size_t pos) const { 36 | if (std::holds_alternative(_val)) { 37 | return std::get(_val)[pos]; 38 | } else { 39 | throw JsonException("not a array"); 40 | } 41 | } 42 | 43 | Json& JsonValue::operator[](size_t pos) { 44 | return const_cast(static_cast(*this)[pos]); 45 | } 46 | 47 | // operator[] for object:O(1) search 48 | const Json& JsonValue::operator[](const std::string& key) const { 49 | if (std::holds_alternative(_val)) { 50 | return std::get(_val).at(key); 51 | } else { 52 | throw JsonException("not a object"); 53 | } 54 | } 55 | 56 | Json& JsonValue::operator[](const std::string& key) { 57 | return const_cast(static_cast(*this)[key]); 58 | } 59 | 60 | // convert interface 61 | std::nullptr_t JsonValue::toNull() const { 62 | try { 63 | return std::get(_val); 64 | } catch (const std::bad_variant_access&) { 65 | throw JsonException("not a null"); 66 | } 67 | } 68 | 69 | bool JsonValue::toBool() const { 70 | try { 71 | return std::get(_val); 72 | } catch (const std::bad_variant_access&) { 73 | throw JsonException("not a bool"); 74 | } 75 | } 76 | 77 | double JsonValue::toDouble() const { 78 | try { 79 | return std::get(_val); 80 | } catch (const std::bad_variant_access&) { 81 | throw JsonException("not a double"); 82 | } 83 | } 84 | 85 | const std::string& JsonValue::toString() const { 86 | try { 87 | return std::get(_val); 88 | } catch (const std::bad_variant_access&) { 89 | throw JsonException("not a string"); 90 | } 91 | } 92 | 93 | const Json::_array& JsonValue::toArray() const { 94 | try { 95 | return std::get(_val); 96 | } catch (const std::bad_variant_access&) { 97 | throw JsonException("not a array"); 98 | } 99 | } 100 | 101 | const Json::_object& JsonValue::toObject() const { 102 | try { 103 | return std::get(_val); 104 | } catch (const std::bad_variant_access&) { 105 | throw JsonException("not a object"); 106 | } 107 | } 108 | 109 | } // namespace miniJson -------------------------------------------------------------------------------- /Source/include/json.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace miniJson { 9 | 10 | enum class JsonType { kNull, kBool, kNumber, kString, kArray, kObject }; 11 | 12 | // PIMPL 13 | // forward declaration for std::unique_ptr 14 | class JsonValue; 15 | 16 | class Json final { 17 | public: // alias declarations 18 | using _array = std::vector; 19 | using _object = std::unordered_map; 20 | 21 | public: // ctor for the various types of JSON value 22 | Json() : Json(nullptr){}; 23 | Json(std::nullptr_t); 24 | Json(bool); 25 | // convert int into double 26 | Json(int val) : Json(1.0 * val) {} 27 | Json(double); 28 | // without this ctor, Json("xx") will call Json(bool) 29 | Json(const char* cstr) : Json(std::string(cstr)) {} 30 | Json(const std::string&); 31 | Json(std::string&&); 32 | Json(const _array&); 33 | Json(_array&&); 34 | Json(const _object&); 35 | Json(_object&&); 36 | 37 | // prevents Json(some_pointer) from accidentally producing a bool. 38 | Json(void*) = delete; 39 | 40 | public: // implicit ctor for objects 41 | // Implicit constructor: map-like objects (std::map, std::unordered_map, 42 | // etc) 43 | template < 44 | class M, 45 | typename std::enable_if< 46 | std::is_constructible< 47 | std::string, 48 | decltype(std::declval().begin()->first)>::value && 49 | std::is_constructible< 50 | Json, decltype(std::declval().begin()->second)>::value, 51 | int>::type = 0> 52 | Json(const M& m) : Json(_object(m.begin(), m.end())) {} 53 | // Implicit constructor: vector-like objects (std::list, std::vector, 54 | // std::set, etc) 55 | template ().begin())>::value, 59 | int>::type = 0> 60 | Json(const V& v) : Json(_array(v.begin(), v.end())) {} 61 | 62 | public: // dtor 63 | ~Json(); 64 | 65 | public: // copy constructor && assignment 66 | Json(const Json&); // deeply copy 67 | Json& operator=(const Json&) noexcept; // copy && swap 68 | 69 | public: // move constructor && assignment 70 | Json(Json&&) noexcept; 71 | Json& operator=(Json&&) noexcept; 72 | 73 | public: // parse && serialize interface 74 | // errMsg can store exception message(all exception will be catched) 75 | static Json parse(const std::string& content, std::string& errMsg) noexcept; 76 | std::string serialize() const noexcept; 77 | 78 | public: // type interface 79 | JsonType getType() const noexcept; 80 | bool isNull() const noexcept; 81 | bool isBool() const noexcept; 82 | bool isNumber() const noexcept; 83 | bool isString() const noexcept; 84 | bool isArray() const noexcept; 85 | bool isObject() const noexcept; 86 | 87 | public: // convert json object into value 88 | bool toBool() const; 89 | double toDouble() const; 90 | const std::string& toString() const; 91 | const _array& toArray() const; 92 | const _object& toObject() const; 93 | 94 | public: // interface for array && object 95 | size_t size() const; 96 | // operator[] for array 97 | Json& operator[](size_t); 98 | const Json& operator[](size_t) const; 99 | // operator[] for object 100 | Json& operator[](const std::string&); 101 | const Json& operator[](const std::string&) const; 102 | 103 | private: // aux interface 104 | void swap(Json&) noexcept; // make copy && swap 105 | std::string serializeString() const noexcept; 106 | std::string serializeArray() const noexcept; 107 | std::string serializeObject() const noexcept; 108 | 109 | private: // data member 110 | std::unique_ptr _jsonValue; 111 | }; 112 | 113 | // non-member function 114 | inline std::ostream& operator<<(std::ostream& os, const Json& json) { 115 | return os << json.serialize(); 116 | } 117 | bool operator==(const Json&, const Json&); 118 | inline bool operator!=(const Json& lhs, const Json& rhs) { 119 | return !(lhs == rhs); 120 | } 121 | 122 | } // namespace miniJson 123 | -------------------------------------------------------------------------------- /Source/src/json.cpp: -------------------------------------------------------------------------------- 1 | #include "json.h" 2 | #include 3 | #include "jsonValue.h" 4 | #include "parse.h" 5 | 6 | namespace miniJson { 7 | 8 | // Json's ctor && dtor 9 | Json::Json(std::nullptr_t) : _jsonValue(std::make_unique(nullptr)) {} 10 | Json::Json(bool val) : _jsonValue(std::make_unique(val)) {} 11 | Json::Json(double val) : _jsonValue(std::make_unique(val)) {} 12 | Json::Json(const std::string& val) 13 | : _jsonValue(std::make_unique(val)) {} 14 | Json::Json(std::string&& val) 15 | : _jsonValue(std::make_unique(std::move(val))) {} 16 | Json::Json(const _array& val) : _jsonValue(std::make_unique(val)) {} 17 | Json::Json(_array&& val) 18 | : _jsonValue(std::make_unique(std::move(val))) {} 19 | Json::Json(const _object& val) : _jsonValue(std::make_unique(val)) {} 20 | Json::Json(_object&& val) 21 | : _jsonValue(std::make_unique(std::move(val))) {} 22 | 23 | Json::~Json() = default; 24 | 25 | // Json's copy constructor 26 | Json::Json(const Json& rhs) { 27 | switch (rhs.getType()) { 28 | case JsonType::kNull: { 29 | _jsonValue = std::make_unique(nullptr); 30 | break; 31 | } 32 | case JsonType::kBool: { 33 | _jsonValue = std::make_unique(rhs.toBool()); 34 | break; 35 | } 36 | case JsonType::kNumber: { 37 | _jsonValue = std::make_unique(rhs.toDouble()); 38 | break; 39 | } 40 | case JsonType::kString: { 41 | _jsonValue = std::make_unique(rhs.toString()); 42 | break; 43 | } 44 | case JsonType::kArray: { 45 | _jsonValue = std::make_unique(rhs.toArray()); 46 | break; 47 | } 48 | case JsonType::kObject: { 49 | _jsonValue = std::make_unique(rhs.toObject()); 50 | break; 51 | } 52 | default: { break; } 53 | } 54 | } 55 | 56 | // Json's copy assignment 57 | Json& Json::operator=(const Json& rhs) noexcept { 58 | // copy && swap 59 | Json temp(rhs); 60 | swap(temp); 61 | return *this; 62 | } 63 | 64 | // Json's move operation=default 65 | Json::Json(Json&& rhs) noexcept = default; 66 | Json& Json::operator=(Json&& rhs) noexcept = default; 67 | 68 | // parse interface(static member function) 69 | Json Json::parse(const std::string& content, std::string& errMsg) noexcept { 70 | try { 71 | Parser p(content); 72 | return p.parse(); 73 | } catch (JsonException& e) { 74 | errMsg = e.what(); 75 | return Json(nullptr); 76 | } 77 | } 78 | 79 | std::string Json::serialize() const noexcept { 80 | switch (_jsonValue->getType()) { 81 | case JsonType::kNull: 82 | return "null"; 83 | case JsonType::kBool: 84 | return _jsonValue->toBool() ? "true" : "false"; 85 | case JsonType::kNumber: 86 | char buf[32]; 87 | snprintf( 88 | buf, sizeof(buf), "%.17g", 89 | _jsonValue 90 | ->toDouble()); // enough to convert a double to a string 91 | return std::string(buf); 92 | case JsonType::kString: 93 | return serializeString(); 94 | case JsonType::kArray: 95 | return serializeArray(); 96 | default: 97 | return serializeObject(); 98 | } 99 | } 100 | 101 | // type interface 102 | JsonType Json::getType() const noexcept { return _jsonValue->getType(); } 103 | 104 | bool Json::isNull() const noexcept { return getType() == JsonType::kNull; } 105 | bool Json::isBool() const noexcept { return getType() == JsonType::kBool; } 106 | bool Json::isNumber() const noexcept { return getType() == JsonType::kNumber; } 107 | bool Json::isString() const noexcept { return getType() == JsonType::kString; } 108 | bool Json::isArray() const noexcept { return getType() == JsonType::kArray; } 109 | bool Json::isObject() const noexcept { return getType() == JsonType::kObject; } 110 | 111 | // parse interface 112 | bool Json::toBool() const { return _jsonValue->toBool(); } 113 | double Json::toDouble() const { return _jsonValue->toDouble(); } 114 | const std::string& Json::toString() const { return _jsonValue->toString(); } 115 | const Json::_array& Json::toArray() const { return _jsonValue->toArray(); } 116 | const Json::_object& Json::toObject() const { return _jsonValue->toObject(); } 117 | 118 | // interface for array and object 119 | size_t Json::size() const { return _jsonValue->size(); } 120 | // operator[] for array 121 | Json& Json::operator[](size_t pos) { return _jsonValue->operator[](pos); } 122 | const Json& Json::operator[](size_t pos) const { 123 | return _jsonValue->operator[](pos); 124 | } 125 | // operator[] for object 126 | Json& Json::operator[](const std::string& key) { 127 | return _jsonValue->operator[](key); 128 | } 129 | const Json& Json::operator[](const std::string& key) const { 130 | return _jsonValue->operator[](key); 131 | } 132 | 133 | // aux interface for copy && swap 134 | void Json::swap(Json& rhs) noexcept { 135 | using std::swap; 136 | swap(_jsonValue, rhs._jsonValue); 137 | } 138 | 139 | // aux interface for serialize 140 | std::string Json::serializeString() const noexcept { 141 | std::string res = "\""; 142 | for (auto e : _jsonValue->toString()) { 143 | switch (e) { 144 | case '\"': 145 | res += "\\\""; 146 | break; 147 | case '\\': 148 | res += "\\\\"; 149 | break; 150 | case '\b': 151 | res += "\\b"; 152 | break; 153 | case '\f': 154 | res += "\\f"; 155 | break; 156 | case '\n': 157 | res += "\\n"; 158 | break; 159 | case '\r': 160 | res += "\\r"; 161 | break; 162 | case '\t': 163 | res += "\\t"; 164 | break; 165 | default: 166 | if (static_cast(e) < 0x20) { 167 | char buf[7]; 168 | sprintf(buf, "\\u%04X", e); 169 | res += buf; 170 | } else 171 | res += e; 172 | } 173 | } 174 | return res + '"'; 175 | } 176 | 177 | std::string Json::serializeArray() const noexcept { 178 | std::string res = "[ "; 179 | for (size_t i = 0; i != _jsonValue->size(); ++i) { 180 | if (i > 0) { 181 | res += ", "; 182 | } 183 | res += (*this)[i].serialize(); 184 | } 185 | return res + " ]"; 186 | } 187 | 188 | std::string Json::serializeObject() const noexcept { 189 | std::string res = "{ "; 190 | bool first = true; // indicate now is the first object 191 | for (auto&& p : _jsonValue->toObject()) { 192 | if (first) { 193 | first = false; 194 | } else { 195 | res += ", "; 196 | } 197 | res += "\"" + p.first + "\""; 198 | res += ": "; 199 | res += p.second.serialize(); 200 | } 201 | return res + " }"; 202 | } 203 | 204 | bool operator==(const Json& lhs, const Json& rhs) { 205 | if (lhs.getType() != rhs.getType()) { 206 | return false; 207 | } 208 | switch (lhs.getType()) { 209 | case JsonType::kNull: { 210 | return true; 211 | } 212 | case JsonType::kBool: { 213 | return lhs.toBool() == rhs.toBool(); 214 | } 215 | case JsonType::kNumber: { 216 | return lhs.toDouble() == rhs.toDouble(); 217 | } 218 | case JsonType::kString: { 219 | return lhs.toString() == rhs.toString(); 220 | } 221 | case JsonType::kArray: { 222 | return lhs.toArray() == rhs.toArray(); 223 | } 224 | case JsonType::kObject: { 225 | return lhs.toObject() == rhs.toObject(); 226 | } 227 | default: { return false; } 228 | } 229 | } 230 | 231 | } // namespace miniJson 232 | -------------------------------------------------------------------------------- /Source/src/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "parse.h" 2 | #include // assert 3 | #include // Huge_Val 4 | #include // strtod 5 | #include // strncmp 6 | #include // runtime_error 7 | 8 | namespace miniJson { 9 | 10 | // skip all whitespace 11 | void Parser::parseWhitespace() noexcept { 12 | while (*_curr == ' ' || *_curr == '\t' || *_curr == '\r' || 13 | *_curr == '\n') { 14 | ++_curr; 15 | } 16 | _start = _curr; 17 | } 18 | 19 | unsigned Parser::parse4hex() { 20 | unsigned u = 0; 21 | for (int i = 0; i != 4; ++i) { 22 | auto ch = static_cast(toupper(*++_curr)); 23 | u <<= 4; 24 | if (ch >= '0' && ch <= '9') { 25 | u |= (ch - '0'); 26 | } else if (ch >= 'A' && ch <= 'F') { 27 | u |= ch - 'A' + 10; 28 | } else { 29 | error("INVALID UNICODE HEX"); 30 | } 31 | } 32 | return u; 33 | } 34 | 35 | std::string Parser::encodeUTF8(unsigned u) noexcept { 36 | std::string utf8; 37 | if (u <= 0x7F) { // 0111,1111 38 | utf8.push_back(static_cast(u & 0xff)); 39 | } else if (u <= 0x7FF) { 40 | utf8.push_back(static_cast(0xc0 | ((u >> 6) & 0xff))); 41 | utf8.push_back(static_cast(0x80 | (u & 0x3f))); 42 | } else if (u <= 0xFFFF) { 43 | utf8.push_back(static_cast(0xe0 | ((u >> 12) & 0xff))); 44 | utf8.push_back(static_cast(0x80 | ((u >> 6) & 0x3f))); 45 | utf8.push_back(static_cast(0x80 | (u & 0x3f))); 46 | } else { 47 | assert(u <= 0x10FFFF); 48 | utf8.push_back(static_cast(0xf0 | ((u >> 18) & 0xff))); 49 | utf8.push_back(static_cast(0x80 | ((u >> 12) & 0x3f))); 50 | utf8.push_back(static_cast(0x80 | ((u >> 6) & 0x3f))); 51 | utf8.push_back(static_cast(0x80 | (u & 0x3f))); 52 | } 53 | return utf8; 54 | } 55 | 56 | std::string Parser::parseRawString() { 57 | std::string str; 58 | while (true) { 59 | switch (*++_curr) { 60 | case '\"': 61 | _start = ++_curr; 62 | return str; 63 | case '\0': 64 | error("MISS QUOTATION MARK"); 65 | default: 66 | if (static_cast(*_curr) < 0x20) 67 | error("INVALID STRING CHAR"); 68 | str.push_back(*_curr); 69 | break; 70 | case '\\': 71 | switch (*++_curr) { 72 | case '\"': 73 | str.push_back('\"'); 74 | break; 75 | case '\\': 76 | str.push_back('\\'); 77 | break; 78 | case '/': 79 | str.push_back('/'); 80 | break; 81 | case 'b': 82 | str.push_back('\b'); 83 | break; 84 | case 'f': 85 | str.push_back('\f'); 86 | break; 87 | case 'n': 88 | str.push_back('\n'); 89 | break; 90 | case 't': 91 | str.push_back('\t'); 92 | break; 93 | case 'r': 94 | str.push_back('\r'); 95 | break; 96 | case 'u': { 97 | unsigned u1 = parse4hex(); 98 | if (u1 >= 0xd800 && u1 <= 0xdbff) { // high surrogate 99 | if (*++_curr != '\\') 100 | error("INVALID UNICODE SURROGATE"); 101 | if (*++_curr != 'u') 102 | error("INVALID UNICODE SURROGATE"); 103 | unsigned u2 = parse4hex(); // low surrogate 104 | if (u2 < 0xdc00 || u2 > 0xdfff) 105 | error("INVALID UNICODE SURROGATE"); 106 | u1 = (((u1 - 0xd800) << 10) | (u2 - 0xdc00)) + 107 | 0x10000; 108 | } 109 | str += encodeUTF8(u1); 110 | } break; 111 | default: 112 | error("INVALID STRING ESCAPE"); 113 | } 114 | break; 115 | } 116 | } 117 | } 118 | 119 | void Parser::error(const std::string& msg) const { 120 | throw JsonException(msg + ": " + _start); 121 | } 122 | 123 | Json Parser::parseValue() { 124 | switch (*_curr) { 125 | case 'n': 126 | return parseLiteral("null"); 127 | case 't': 128 | return parseLiteral("true"); 129 | case 'f': 130 | return parseLiteral("false"); 131 | case '\"': 132 | return parseString(); 133 | case '[': 134 | return parseArray(); 135 | case '{': 136 | return parseObject(); 137 | case '\0': 138 | error("EXPECT VALUE"); 139 | default: 140 | return parseNumber(); 141 | } 142 | } 143 | 144 | Json Parser::parseLiteral(const std::string& literal) { 145 | // try to parse null && true && false 146 | if (strncmp(_curr, literal.c_str(), literal.size()) != 0) 147 | error("INVALID VALUE"); 148 | _curr += literal.size(); 149 | _start = _curr; 150 | switch (literal[0]) { 151 | case 't': 152 | return Json(true); 153 | case 'f': 154 | return Json(false); 155 | default: 156 | return Json(nullptr); 157 | } 158 | } 159 | 160 | Json Parser::parseNumber() { 161 | if (*_curr == '-') ++_curr; 162 | if (*_curr == '0') 163 | ++_curr; 164 | else { 165 | if (!is1to9(*_curr)) error("INVALID VALUE"); 166 | while (is0to9(*++_curr)) 167 | ; // pass all number character 168 | } 169 | if (*_curr == '.') { 170 | if (!is0to9(*++_curr)) // there must be a number character after '.' 171 | error("INVALID VALUE"); 172 | while (is0to9(*++_curr)) 173 | ; 174 | } 175 | if (toupper(*_curr) == 'E') { 176 | ++_curr; 177 | if (*_curr == '-' || *_curr == '+') ++_curr; 178 | if (!is0to9(*_curr)) error("INVALID VALUE"); 179 | while (is0to9(*++_curr)) 180 | ; 181 | } 182 | // When we make sure that the current text is a number, 183 | // call the library function strtod 184 | double val = strtod(_start, nullptr); 185 | if (fabs(val) == HUGE_VAL) error("NUMBER TOO BIG"); 186 | _start = _curr; 187 | return Json(val); 188 | } 189 | 190 | Json Parser::parseString() { return Json(parseRawString()); } 191 | 192 | Json Parser::parseArray() { 193 | Json::_array arr; 194 | ++_curr; // skip '[' 195 | parseWhitespace(); 196 | if (*_curr == ']') { 197 | _start = ++_curr; 198 | return Json(arr); 199 | } 200 | while (true) { 201 | parseWhitespace(); 202 | arr.push_back(parseValue()); // recursive 203 | parseWhitespace(); 204 | if (*_curr == ',') 205 | ++_curr; 206 | else if (*_curr == ']') { 207 | _start = ++_curr; 208 | return Json(arr); 209 | } else 210 | error("MISS COMMA OR SQUARE BRACKET"); 211 | } 212 | } 213 | 214 | Json Parser::parseObject() { 215 | Json::_object obj; 216 | ++_curr; 217 | parseWhitespace(); 218 | if (*_curr == '}') { 219 | _start = ++_curr; 220 | return Json(obj); 221 | } 222 | while (true) { 223 | parseWhitespace(); 224 | if (*_curr != '"') error("MISS KEY"); 225 | std::string key = parseRawString(); 226 | parseWhitespace(); 227 | if (*_curr++ != ':') error("MISS COLON"); 228 | parseWhitespace(); 229 | Json val = parseValue(); 230 | obj.insert({key, val}); 231 | parseWhitespace(); 232 | if (*_curr == ',') 233 | ++_curr; 234 | else if (*_curr == '}') { 235 | _start = ++_curr; 236 | return Json(obj); 237 | } else 238 | error("MISS COMMA OR CURLY BRACKET"); 239 | } 240 | } 241 | 242 | Json Parser::parse() { 243 | // JSON-text = ws value ws 244 | // ws = *(%x20 / %x09 / %x0A / %x0D) 245 | parseWhitespace(); 246 | Json json = parseValue(); 247 | parseWhitespace(); 248 | if (*_curr) 249 | // some character still exists after the end whitespace 250 | error("ROOT NOT SINGULAR"); 251 | return json; 252 | } 253 | 254 | } // namespace miniJson -------------------------------------------------------------------------------- /UnitTest/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gtest/gtest.h" 3 | #include "json.h" 4 | 5 | using namespace miniJson; 6 | using namespace std; 7 | 8 | Json parseOk(const string& strJson) { 9 | string errMsg; 10 | Json json = Json::parse(strJson, errMsg); 11 | EXPECT_EQ(errMsg, ""); 12 | return json; 13 | } 14 | 15 | #define testError(expect, strJson) \ 16 | do { \ 17 | string errMsg; \ 18 | Json json = Json::parse(strJson, errMsg); \ 19 | auto pos = errMsg.find_first_of(":"); \ 20 | auto actual = errMsg.substr(0, pos); \ 21 | EXPECT_EQ(actual, expect); \ 22 | } while (0) 23 | 24 | #define testNull(strJson) \ 25 | do { \ 26 | Json json = parseOk(strJson); \ 27 | EXPECT_TRUE(json.isNull()); \ 28 | } while (0) 29 | 30 | #define testBool(expect, content) \ 31 | do { \ 32 | Json json = parseOk(content); \ 33 | EXPECT_TRUE(json.isBool()); \ 34 | EXPECT_EQ(json.toBool(), expect); \ 35 | json = Json(!expect); \ 36 | EXPECT_EQ(json.toBool(), !expect); \ 37 | } while (0) 38 | 39 | #define testNumber(expect, strJson) \ 40 | do { \ 41 | Json json = parseOk(strJson); \ 42 | EXPECT_TRUE(json.isNumber()); \ 43 | EXPECT_EQ(json.toDouble(), expect); \ 44 | } while (0) 45 | 46 | #define testString(expect, strJson) \ 47 | do { \ 48 | Json json = parseOk(strJson); \ 49 | EXPECT_TRUE(json.isString()); \ 50 | EXPECT_EQ(json.toString(), expect); \ 51 | } while (0) 52 | 53 | #define testRoundtrip(expect) \ 54 | do { \ 55 | Json json = parseOk(expect); \ 56 | string actual = json.serialize(); \ 57 | if (json.isNumber()) \ 58 | EXPECT_EQ(strtod(actual.c_str(), nullptr), \ 59 | strtod(expect, nullptr)); \ 60 | else \ 61 | EXPECT_EQ(actual, expect); \ 62 | } while (0) 63 | 64 | TEST(Str2Json, JsonNull) { 65 | testNull("null"); 66 | testNull(" null\n\r\t"); 67 | } 68 | 69 | TEST(Str2Json, JsonBool) { 70 | testBool(true, "true"); 71 | testBool(false, "false"); 72 | } 73 | 74 | TEST(Str2Json, JsonNumber) { 75 | testNumber(0.0, "0"); 76 | testNumber(0.0, "-0"); 77 | testNumber(0.0, "0.0"); 78 | testNumber(1.0, "1"); 79 | testNumber(-1.0, "-1"); 80 | testNumber(1.5, "1.5"); 81 | testNumber(-1.5, "-1.5"); 82 | testNumber(3.1416, "3.1416"); 83 | testNumber(1E10, "1E10"); 84 | testNumber(1e10, "1e10"); 85 | testNumber(1E+10, "1E+10"); 86 | testNumber(1E-10, "1E-10"); 87 | testNumber(-1E10, "-1E10"); 88 | testNumber(-1e10, "-1e10"); 89 | testNumber(-1E+10, "-1E+10"); 90 | testNumber(-1E-10, "-1E-10"); 91 | testNumber(1.234E+10, "1.234E+10"); 92 | testNumber(1.234E-10, "1.234E-10"); 93 | testNumber(5.0E-324, "5e-324"); 94 | testNumber(0, "1e-10000"); 95 | testNumber(1.0000000000000002, "1.0000000000000002"); 96 | testNumber(4.9406564584124654e-324, "4.9406564584124654e-324"); 97 | testNumber(-4.9406564584124654e-324, "-4.9406564584124654e-324"); 98 | testNumber(2.2250738585072009e-308, "2.2250738585072009e-308"); 99 | testNumber(-2.2250738585072009e-308, "-2.2250738585072009e-308"); 100 | testNumber(2.2250738585072014e-308, "2.2250738585072014e-308"); 101 | testNumber(-2.2250738585072014e-308, "-2.2250738585072014e-308"); 102 | testNumber(1.7976931348623157e+308, "1.7976931348623157e+308"); 103 | testNumber(-1.7976931348623157e+308, "-1.7976931348623157e+308"); 104 | string errMsg; 105 | Json json = Json::parse("1.2e+12", errMsg); 106 | EXPECT_TRUE(json.isNumber()); 107 | json = Json(3.1415); 108 | EXPECT_EQ(3.1415, json.toDouble()); 109 | } 110 | 111 | TEST(Str2Json, JsonString) { 112 | testString("", "\"\""); 113 | testString("Hello", "\"Hello\""); 114 | testString("Hello\nWorld", "\"Hello\\nWorld\""); 115 | testString("\" \\ / \b \f \n \r \t", 116 | "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); 117 | string s = "Hello"; 118 | s.push_back('\0'); 119 | s += "World"; 120 | testString(s, "\"Hello\\u0000World\""); 121 | testString("\x24", "\"\\u0024\""); 122 | testString("\xC2\xA2", "\"\\u00A2\""); 123 | testString("\xE2\x82\xAC", "\"\\u20AC\""); 124 | testString("\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); 125 | testString("\xF0\x9D\x84\x9E", "\"\\ud834\\udd1e\""); 126 | string errMsg; 127 | Json json = Json::parse("\"something\"", errMsg); 128 | json = Json("another thing"); 129 | EXPECT_EQ(json.toString(), "another thing"); 130 | } 131 | 132 | TEST(Str2Json, JsonArray) { 133 | Json json = parseOk("[ ]"); 134 | EXPECT_TRUE(json.isArray()); 135 | EXPECT_EQ(json.size(), 0); 136 | 137 | json = parseOk("[ null , false , true , 123 , \"abc\" ]"); 138 | EXPECT_TRUE(json.isArray()); 139 | EXPECT_EQ(json.size(), 5); 140 | EXPECT_EQ(json[0], Json(nullptr)); 141 | EXPECT_EQ(json[1], Json(false)); 142 | EXPECT_EQ(json[2], Json(true)); 143 | EXPECT_EQ(json[3], Json(123.0)); 144 | EXPECT_EQ(json[4], Json("abc")); 145 | 146 | json = parseOk("[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]"); 147 | EXPECT_TRUE(json.isArray()); 148 | EXPECT_EQ(json.size(), 4); 149 | 150 | EXPECT_TRUE(json[0].isArray()); 151 | EXPECT_EQ(json[0].size(), 0); 152 | 153 | EXPECT_TRUE(json[1].isArray()); 154 | EXPECT_EQ(json[1].size(), 1); 155 | EXPECT_EQ(json[1][0].toDouble(), 0); 156 | 157 | EXPECT_TRUE(json[2].isArray()); 158 | EXPECT_EQ(json[2].size(), 2); 159 | EXPECT_EQ(json[2][0].toDouble(), 0); 160 | EXPECT_EQ(json[2][1].toDouble(), 1); 161 | 162 | EXPECT_TRUE(json[3].isArray()); 163 | EXPECT_EQ(json[3].size(), 3); 164 | EXPECT_EQ(json[3][0].toDouble(), 0); 165 | EXPECT_EQ(json[3][1].toDouble(), 1); 166 | EXPECT_EQ(json[3][2].toDouble(), 2); 167 | } 168 | 169 | TEST(Str2Json, JsonObject) { 170 | Json json = parseOk("{ }"); 171 | EXPECT_TRUE(json.isObject()); 172 | EXPECT_EQ(json.size(), 0); 173 | 174 | json = parseOk( 175 | " { " 176 | "\"n\" : null , " 177 | "\"f\" : false , " 178 | "\"t\" : true , " 179 | "\"i\" : 123 , " 180 | "\"s\" : \"abc\", " 181 | "\"a\" : [ 1, 2, 3 ]," 182 | "\"o\" : { \"1\" : 1, \"2\" : 2, \"3\" : 3 }" 183 | " } "); 184 | EXPECT_TRUE(json.isObject()); 185 | EXPECT_EQ(json.size(), 7); 186 | 187 | EXPECT_TRUE(json["n"].isNull()); 188 | 189 | EXPECT_TRUE(json["f"].isBool()); 190 | EXPECT_EQ(json["f"].toBool(), false); 191 | 192 | EXPECT_TRUE(json["t"].isBool()); 193 | EXPECT_EQ(json["t"].toBool(), true); 194 | 195 | EXPECT_TRUE(json["i"].isNumber()); 196 | EXPECT_EQ(json["i"].toDouble(), 123.0); 197 | 198 | EXPECT_TRUE(json["s"].isString()); 199 | EXPECT_EQ(json["s"].toString(), "abc"); 200 | 201 | EXPECT_TRUE(json["a"].isArray()); 202 | EXPECT_EQ(json["a"].size(), 3); 203 | 204 | EXPECT_TRUE(json["o"].isObject()); 205 | EXPECT_EQ(json["o"].size(), 3); 206 | } 207 | 208 | TEST(RoundTrip, literal) { 209 | testRoundtrip("null"); 210 | testRoundtrip("true"); 211 | testRoundtrip("false"); 212 | } 213 | 214 | TEST(RoundTrip, JsonNumber) { 215 | testRoundtrip("0"); 216 | testRoundtrip("-0"); 217 | testRoundtrip("1"); 218 | testRoundtrip("-0"); 219 | testRoundtrip("1.5"); 220 | testRoundtrip("-1.5"); 221 | testRoundtrip("3.25"); 222 | testRoundtrip("1e+20"); 223 | testRoundtrip("1.234e+20"); 224 | testRoundtrip("1.234e-20"); 225 | testRoundtrip("1.0000000000000002"); 226 | testRoundtrip("4.9406564584124654e-324"); 227 | testRoundtrip("-4.9406564584124654e-324"); 228 | testRoundtrip("2.2250738585072009e-308"); 229 | testRoundtrip("-2.2250738585072009e-308"); 230 | testRoundtrip("2.2250738585072014e-308"); 231 | testRoundtrip("-2.2250738585072014e-308"); 232 | testRoundtrip("1.7976931348623157e+308"); 233 | testRoundtrip("-1.7976931348623157e+308"); 234 | } 235 | 236 | TEST(RoundTrip, JsonString) { 237 | testRoundtrip("\"\""); 238 | testRoundtrip("\"Hello\""); 239 | testRoundtrip("\"Hello\\nWorld\""); 240 | testRoundtrip("\"\\\" \\\\ / \\b \\f \\n \\r \\t\""); 241 | testRoundtrip("\"Hello\\u0000World\""); 242 | } 243 | 244 | TEST(RoundTrip, JsonArray) { 245 | testRoundtrip("[ ]"); 246 | testRoundtrip("[ null, false, true, 123, \"abc\", [ 1, 2, 3 ] ]"); 247 | } 248 | 249 | // TODO:: 250 | // Temporarily failed to pass RoundTrip test 251 | // Because MiniJson store a Json Object as a hashmap 252 | // Therefore, the serialized string may be inconsistent with the original input 253 | // But it does not affect the correctness of json's serialization 254 | TEST(RoundTrip, DISABLED_JsonObject) { 255 | testRoundtrip("{ }"); 256 | testRoundtrip(( 257 | R"({ "n": null, "f": false, "t": true, "i": 123, "a": [ 1, 2, 3 ], "s": "abc", "o": { "1": 1, "2": 2, "3": 3 } })")); 258 | } 259 | 260 | TEST(Error, ExpectValue) { 261 | testError("EXPECT VALUE", ""); 262 | testError("EXPECT VALUE", " "); 263 | } 264 | 265 | TEST(Error, InvalidValue) { 266 | testError("INVALID VALUE", "nul"); 267 | testError("INVALID VALUE", "?"); 268 | testError("INVALID VALUE", "+0"); 269 | testError("INVALID VALUE", "+1"); 270 | testError("INVALID VALUE", ".123"); 271 | testError("INVALID VALUE", "1."); 272 | testError("INVALID VALUE", "inf"); 273 | testError("INVALID VALUE", "INF"); 274 | testError("INVALID VALUE", "NAN"); 275 | testError("INVALID VALUE", "nan"); 276 | testError("INVALID VALUE", "[1,]"); 277 | testError("INVALID VALUE", "[\"a\", nul]"); 278 | } 279 | 280 | TEST(Error, RootNotSingular) { 281 | testError("ROOT NOT SINGULAR", "null x"); 282 | testError("ROOT NOT SINGULAR", "0123"); 283 | testError("ROOT NOT SINGULAR", "0x0"); 284 | testError("ROOT NOT SINGULAR", "0x123"); 285 | } 286 | 287 | TEST(Error, NumberTooBig) { 288 | testError("NUMBER TOO BIG", "1e309"); 289 | testError("NUMBER TOO BIG", "-1e309"); 290 | } 291 | 292 | TEST(Error, MissQuotationMark) { 293 | testError("MISS QUOTATION MARK", "\""); 294 | testError("MISS QUOTATION MARK", "\"abc"); 295 | } 296 | 297 | TEST(Error, InvalidStringEscape) { 298 | testError("INVALID STRING ESCAPE", "\"\\v\""); 299 | testError("INVALID STRING ESCAPE", "\"\\'\""); 300 | testError("INVALID STRING ESCAPE", "\"\\0\""); 301 | testError("INVALID STRING ESCAPE", "\"\\x12\""); 302 | } 303 | 304 | TEST(Error, InvalidStringChar) { 305 | testError("INVALID STRING CHAR", "\"\x01\""); 306 | testError("INVALID STRING CHAR", "\"\x1F\""); 307 | } 308 | 309 | TEST(Error, InvalidUnicodeHex) { 310 | testError("INVALID UNICODE HEX", "\"\\u\""); 311 | testError("INVALID UNICODE HEX", "\"\\u0\""); 312 | testError("INVALID UNICODE HEX", "\"\\u01\""); 313 | testError("INVALID UNICODE HEX", "\"\\u012\""); 314 | testError("INVALID UNICODE HEX", "\"\\u/000\""); 315 | testError("INVALID UNICODE HEX", "\"\\uG000\""); 316 | testError("INVALID UNICODE HEX", "\"\\u0/00\""); 317 | testError("INVALID UNICODE HEX", "\"\\u0G00\""); 318 | testError("INVALID UNICODE HEX", "\"\\u000/\""); 319 | testError("INVALID UNICODE HEX", "\"\\u00G/\""); 320 | testError("INVALID UNICODE HEX", "\"\\u 123/\""); 321 | } 322 | 323 | TEST(Error, InvalidUnicodeSurrogate) { 324 | testError("INVALID UNICODE SURROGATE", "\"\\uD800\""); 325 | testError("INVALID UNICODE SURROGATE", "\"\\uDBFF\""); 326 | testError("INVALID UNICODE SURROGATE", "\"\\uD800\\\\\\"); 327 | testError("INVALID UNICODE SURROGATE", "\"\\uD800\\uDBFF\""); 328 | testError("INVALID UNICODE SURROGATE", "\"\\uD800\\uE000\""); 329 | } 330 | 331 | TEST(Error, MissCommaOrSquareBracket) { 332 | testError("MISS COMMA OR SQUARE BRACKET", "[1"); 333 | testError("MISS COMMA OR SQUARE BRACKET", "[1}"); 334 | testError("MISS COMMA OR SQUARE BRACKET", "[1 2"); 335 | testError("MISS COMMA OR SQUARE BRACKET", "[[]"); 336 | } 337 | 338 | TEST(Error, MissKey) { 339 | testError("MISS KEY", "{:1,"); 340 | testError("MISS KEY", "{1:1,"); 341 | testError("MISS KEY", "{true:1,"); 342 | testError("MISS KEY", "{false:1,"); 343 | testError("MISS KEY", "{null:1,"); 344 | testError("MISS KEY", "{[]:1,"); 345 | testError("MISS KEY", "{{}:1,"); 346 | testError("MISS KEY", "{\"a\":1,"); 347 | } 348 | 349 | TEST(Error, MissColon) { 350 | testError("MISS COLON", "{\"a\"}"); 351 | testError("MISS COLON", "{\"a\",\"b\"}"); 352 | } 353 | 354 | TEST(Error, MissCommaOrCurlyBracket) { 355 | testError("MISS COMMA OR CURLY BRACKET", "{\"a\":1"); 356 | testError("MISS COMMA OR CURLY BRACKET", "{\"a\":1]"); 357 | testError("MISS COMMA OR CURLY BRACKET", "{\"a\":1 \"b\""); 358 | testError("MISS COMMA OR CURLY BRACKET", "{\"a\":{}"); 359 | } 360 | 361 | TEST(Json, Ctor) { 362 | { 363 | Json json; 364 | EXPECT_TRUE(json.isNull()); 365 | } 366 | { 367 | Json json(nullptr); 368 | EXPECT_TRUE(json.isNull()); 369 | } 370 | { 371 | Json json(true); 372 | EXPECT_TRUE(json.isBool()); 373 | EXPECT_EQ(json.toBool(), true); 374 | 375 | Json json1(false); 376 | EXPECT_TRUE(json1.isBool()); 377 | EXPECT_EQ(json1.toBool(), false); 378 | } 379 | { 380 | Json json(0); 381 | EXPECT_TRUE(json.isNumber()); 382 | EXPECT_EQ(json.toDouble(), 0); 383 | 384 | Json json1(100.1); 385 | EXPECT_TRUE(json1.isNumber()); 386 | EXPECT_EQ(json1.toDouble(), 100.1); 387 | } 388 | { 389 | Json json("hello"); 390 | EXPECT_TRUE(json.isString()); 391 | EXPECT_EQ(json.toString(), "hello"); 392 | } 393 | { 394 | vector arr{Json(nullptr), Json(true), Json(1.2)}; 395 | Json json(arr); 396 | EXPECT_TRUE(json.isArray()); 397 | EXPECT_TRUE(json[0].isNull()); 398 | } 399 | { 400 | unordered_map obj; 401 | obj.insert({"hello", Json(nullptr)}); 402 | obj.insert({"world", Json("!!")}); 403 | Json json(obj); 404 | EXPECT_TRUE(json.isObject()); 405 | EXPECT_TRUE(json["world"].isString()); 406 | } 407 | { 408 | Json json = Json::_object{ 409 | {"key1", "value1"}, 410 | {"key2", false}, 411 | {"key3", Json::_array{1, 2, 3}}, 412 | }; 413 | EXPECT_TRUE(json.isObject()); 414 | EXPECT_TRUE(json["key1"].isString()); 415 | EXPECT_EQ(json["key1"].toString(), "value1"); 416 | EXPECT_TRUE(json["key2"].isBool()); 417 | EXPECT_EQ(json["key2"].toBool(), false); 418 | EXPECT_TRUE(json["key3"].isArray()); 419 | EXPECT_EQ(json["key3"][1].toDouble(), 2); 420 | } 421 | } --------------------------------------------------------------------------------