add exception, gtest and some parse function

This commit is contained in:
Junior
2024-01-03 17:18:05 +08:00
parent d2a9a90e46
commit cd80c3b2a0
11 changed files with 403 additions and 39 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "googletest"]
path = googletest
url = https://github.com/google/googletest.git
+7 -1
View File
@@ -1,2 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" /> <module classpath="CMake" type="CPP_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="D:\Apps\Anaconda3" />
</facet>
</component>
</module>
-7
View File
@@ -9,13 +9,6 @@
</mappings> </mappings>
</serverdata> </serverdata>
</paths> </paths>
<paths name="WSL (a4a5868f-4653-4e3e-a1e4-92d5c2007084)">
<serverdata>
<mappings>
<mapping local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
<paths name="WSL (ab0fb59d-77f8-4703-9979-f3a81cca500e)"> <paths name="WSL (ab0fb59d-77f8-4703-9979-f3a81cca500e)">
<serverdata> <serverdata>
<mappings> <mappings>
+3
View File
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Black">
<option name="sdkName" value="D:\Apps\Anaconda3" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project> </project>
+9 -3
View File
@@ -1,8 +1,14 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
project(SimpleJson) project(SimpleJson)
set(GOOGLETEST_VERSION 1.14.0)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) add_subdirectory(./googletest/googletest)
enable_testing()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src ./googletest/googletest/include)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/lib)
add_executable(SimpleJson main.cpp add_executable(SimpleJson main.cpp
src/json.cpp src/json.cpp
src/json.h) src/json.h
src/exception.h)
target_link_libraries(${PROJECT_NAME} gtest)
Submodule
+1
Submodule googletest added at dddb219c3e
+41 -3
View File
@@ -1,6 +1,44 @@
#include <iostream> #include <iostream>
#include "gtest/gtest.h"
#include "src/json.h"
int main() { TEST(TestCase, testJsonNodeSetDoule) {
std::cout << "Hello, World!" << std::endl; simpleJson::JsonNode node(simpleJson::JSON_TYPE_DOUBLE, "key");
return 0; node.set_value(1.0);
EXPECT_EQ(node.as_int(), 1);
}
TEST(TestCase, testJsonNodeSetString) {
simpleJson::JsonNode node(simpleJson::JSON_TYPE_STRING, "key");
node.set_value("1.0");
const char* s = node.as_string().c_str();
EXPECT_STREQ(s,"1.0");
}
TEST(TestCase, testJsonNodeSetError) {
simpleJson::JsonNode node(simpleJson::JSON_TYPE_INT, "key");
EXPECT_THROW(node.set_value("1.0"), std::runtime_error);
}
TEST(TestCase, testJsonNodeSetKey) {
simpleJson::JsonNode node(simpleJson::JSON_TYPE_INT, "key");
node.set_key("key1");
EXPECT_STREQ(node.get_key().c_str(), "key1");
}
TEST(TestCase,testJsonNodeSetBool) {
simpleJson::JsonNode node(simpleJson::JSON_TYPE_BOOL, "key");
node.set_value(true);
EXPECT_EQ(node.as_bool(), true);
}
TEST(TestCase, testParseStr) {
std::string json_str = "{\"key\":\"1\"}";
simpleJson::JsonParser parser(json_str);
EXPECT_EQ(parser.get_root().obj_get("key").as_string(), "1");
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
} }
+88
View File
@@ -0,0 +1,88 @@
//
// Created by jun on 2023/12/30.
//
#ifndef SIMPLEJSON_EXCEPTION_H
#define SIMPLEJSON_EXCEPTION_H
#include <exception>
#include <string>
#include <string_view>
#include <sstream>
namespace simpleJson {
const std::string_view SYNTAX_ERROR = "Syntax Error";
const std::string_view INVALID_TYPE = "Invalid Type";
class JsonException : public std::exception {
protected:
std::string message;
public:
explicit JsonException(std::string_view str = "JsonException") : message{str} {}
virtual ~JsonException() = default;
};
class JsonTypeError : public JsonException {
protected:
std::string function_name;
JsonValueTypeEnum expected_type;
JsonValueTypeEnum actual_type;
public:
JsonTypeError(std::string_view str, const char *function_name, JsonValueTypeEnum expected,
JsonValueTypeEnum actual) : JsonException{str}, function_name(function_name),
expected_type(expected), actual_type(actual) {}
const char *what() {
std::stringstream ss;
ss << "[JsonTypeError]" << message << ":" << function_name << " expect " << JSON_TYPE_STR[expected_type]
<< " but found " << JSON_TYPE_STR[actual_type];
return ss.str().c_str();
}
};
class JsonParseError : public JsonException {
protected:
int line, column;
public:
JsonParseError(int line, int column, std::string_view str) : JsonException{str}, line{line}, column{column} {}
JsonParseError(std::string_view str) : JsonException{str} {}
const char *what() {
std::stringstream ss;
ss << "[JsonParseError]" << message << " at line " << line << " column " << column;
return ss.str().c_str();
}
};
class JsonInvalidSym : public JsonParseError {
protected:
char detail;
public:
JsonInvalidSym(int line, int column, char detail) : JsonParseError{line, column, SYNTAX_ERROR},
detail(detail) {}
const char *what() {
std::stringstream ss;
ss << "[JsonParseError]" << message << " :invalid symbol " << detail << " at line " << line << " column "
<< column << " detail " << detail;
return ss.str().c_str();
}
};
class JsonInvalidEscapedSym : public JsonInvalidSym {
public:
JsonInvalidEscapedSym(int line, int column, char detail) : JsonInvalidSym{line, column, detail} {}
const char *what() {
std::stringstream ss;
ss << "[JsonParseError]" << message << " :invalid escaped symbol " << detail << " at line " << line
<< " column " << column << " detail " << detail;
return ss.str().c_str();
}
};
}
#endif //SIMPLEJSON_EXCEPTION_H
+241 -24
View File
@@ -3,6 +3,7 @@
// //
#include "json.h" #include "json.h"
#include "exception.h"
namespace simpleJson { namespace simpleJson {
@@ -149,7 +150,8 @@ namespace simpleJson {
setValueType(JSON_TYPE_LIST); setValueType(JSON_TYPE_LIST);
} }
if (type != JSON_TYPE_LIST) { if (type != JSON_TYPE_LIST) {
throw std::runtime_error("JsonNode::set_list() error: type is not list"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_LIST, type);
} }
try { try {
clear(); clear();
@@ -166,7 +168,7 @@ namespace simpleJson {
JsonNode &JsonNode::list_get(int index) { JsonNode &JsonNode::list_get(int index) {
if (type != JSON_TYPE_LIST) { if (type != JSON_TYPE_LIST) {
throw std::runtime_error("JsonNode::list_get() error: type is not list"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_LIST, type);
} else if (index < 0 || index >= children_size) { } else if (index < 0 || index >= children_size) {
throw std::runtime_error("JsonNode::list_get() error: index out of range"); throw std::runtime_error("JsonNode::list_get() error: index out of range");
} else { } else {
@@ -180,7 +182,7 @@ namespace simpleJson {
void JsonNode::list_insert(int index, simpleJson::JsonNodePtr node) { void JsonNode::list_insert(int index, simpleJson::JsonNodePtr node) {
if (type != JSON_TYPE_LIST) { if (type != JSON_TYPE_LIST) {
throw std::runtime_error("JsonNode::list_insert() error: type is not list"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_LIST, type);
} else if (index < 0 || index > children_size) { } else if (index < 0 || index > children_size) {
throw std::runtime_error("JsonNode::list_insert() error: index out of range"); throw std::runtime_error("JsonNode::list_insert() error: index out of range");
} else { } else {
@@ -204,7 +206,8 @@ namespace simpleJson {
void JsonNode::list_erase(int index) { void JsonNode::list_erase(int index) {
if (type != JSON_TYPE_LIST) { if (type != JSON_TYPE_LIST) {
throw std::runtime_error("JsonNode::list_erase() error: type is not list"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_LIST, type);
} else if (index < 0 || index >= children_size) { } else if (index < 0 || index >= children_size) {
throw std::runtime_error("JsonNode::list_erase() error: index out of range"); throw std::runtime_error("JsonNode::list_erase() error: index out of range");
} else { } else {
@@ -231,7 +234,7 @@ namespace simpleJson {
bool JsonNode::obj_has_item(const std::string &key) { bool JsonNode::obj_has_item(const std::string &key) {
if (type != JSON_TYPE_OBJECT) { if (type != JSON_TYPE_OBJECT) {
throw std::runtime_error("JsonNode::obj_has_item() error: type is not object"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_OBJECT, type);
} else { } else {
JsonNodePtr p = child_head; JsonNodePtr p = child_head;
while (p != nullptr) { while (p != nullptr) {
@@ -244,9 +247,9 @@ namespace simpleJson {
} }
} }
JsonNode& JsonNode::obj_get(const std::string &key) { JsonNode &JsonNode::obj_get(const std::string &key) {
if (type != JSON_TYPE_OBJECT) { if (type != JSON_TYPE_OBJECT) {
throw std::runtime_error("JsonNode::obj_get() error: type is not object"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_OBJECT, type);
} else { } else {
JsonNodePtr p = child_head; JsonNodePtr p = child_head;
while (p != nullptr) { while (p != nullptr) {
@@ -255,13 +258,13 @@ namespace simpleJson {
} }
p = p->next; p = p->next;
} }
throw std::runtime_error("JsonNode::obj_get() error: key \""+key+"\" not found"); throw std::runtime_error("JsonNode::obj_get() error: key \"" + key + "\" not found");
} }
} }
void JsonNode::obj_set(simpleJson::JsonNodePtr node) { void JsonNode::obj_set(simpleJson::JsonNodePtr node) {
if (type != JSON_TYPE_OBJECT) { if (type != JSON_TYPE_OBJECT) {
throw std::runtime_error("JsonNode::obj_set() error: type is not object"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_OBJECT, type);
} else { } else {
if (obj_has_item(node->key)) { if (obj_has_item(node->key)) {
obj_get(node->key) = *node; obj_get(node->key) = *node;
@@ -278,7 +281,7 @@ namespace simpleJson {
if (key.size() != 0 && _print_key) { if (key.size() != 0 && _print_key) {
_dst_str += "\"" + key + "\":"; _dst_str += "\"" + key + "\":";
} }
int dep = _one_line ? 0 : _tab_dep+1; int dep = _one_line ? 0 : _tab_dep + 1;
switch (type) { switch (type) {
case JSON_TYPE_INT: { case JSON_TYPE_INT: {
_dst_str += std::to_string(value.int_value); _dst_str += std::to_string(value.int_value);
@@ -346,12 +349,12 @@ namespace simpleJson {
} }
JsonNode& JsonNode::operator[](const std::string & key) { JsonNode &JsonNode::operator[](const std::string &key) {
if (type == JSON_TYPE_NULL) { if (type == JSON_TYPE_NULL) {
setValueType(JSON_TYPE_OBJECT); setValueType(JSON_TYPE_OBJECT);
} }
if (type != JSON_TYPE_OBJECT) { if (type != JSON_TYPE_OBJECT) {
throw std::runtime_error("JsonNode::operator[]() error: type is not object"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_OBJECT, type);
} else { } else {
if (obj_has_item(key)) { if (obj_has_item(key)) {
return obj_get(key); return obj_get(key);
@@ -363,12 +366,12 @@ namespace simpleJson {
} }
} }
JsonNode& JsonNode::operator[](int index) { JsonNode &JsonNode::operator[](int index) {
if (type == JSON_TYPE_NULL) { if (type == JSON_TYPE_NULL) {
setValueType(JSON_TYPE_LIST); setValueType(JSON_TYPE_LIST);
} }
if (type != JSON_TYPE_LIST) { if (type != JSON_TYPE_LIST) {
throw std::runtime_error("JsonNode::operator[]() error: type is not list"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_LIST, type);
} else { } else {
if (index < 0 || index >= children_size) { if (index < 0 || index >= children_size) {
throw std::runtime_error("JsonNode::operator[]() error: index out of range"); throw std::runtime_error("JsonNode::operator[]() error: index out of range");
@@ -382,7 +385,7 @@ namespace simpleJson {
if (type == JSON_TYPE_NULL) { if (type == JSON_TYPE_NULL) {
setValueType(JSON_TYPE_DOUBLE); setValueType(JSON_TYPE_DOUBLE);
} }
if (type == JSON_TYPE_DOUBLE) { if (type == JSON_TYPE_DOUBLE) {
value.double_value = _value; value.double_value = _value;
return _value; return _value;
} else if (type == JSON_TYPE_INT) { } else if (type == JSON_TYPE_INT) {
@@ -392,7 +395,7 @@ namespace simpleJson {
value.bool_value = _value > 0; value.bool_value = _value > 0;
return _value; return _value;
} else { } else {
throw std::runtime_error("JsonNode::operator=(double) error: type is not number"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_INT, type);
} }
} }
@@ -411,7 +414,7 @@ namespace simpleJson {
return 0; return 0;
} }
default: { default: {
throw std::runtime_error("JsonNode::as_int() error: type is not number"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_INT, type);
} }
} }
} }
@@ -431,7 +434,7 @@ namespace simpleJson {
return 0.0; return 0.0;
} }
default: { default: {
throw std::runtime_error("JsonNode::as_double() error: type is not number"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_DOUBLE, type);
} }
} }
} }
@@ -454,12 +457,12 @@ namespace simpleJson {
return "NULL"; return "NULL";
} }
default: { default: {
throw std::runtime_error("JsonNode::as_string() error: type is not string"); throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_STRING, type);
} }
} }
} }
std::vector<JsonNodePtr> JsonNode::as_vector() { std::vector<JsonNodePtr> JsonNode::as_vector() {
std::vector<JsonNodePtr> vec; std::vector<JsonNodePtr> vec;
JsonNodePtr p = child_head; JsonNodePtr p = child_head;
while (p != nullptr) { while (p != nullptr) {
@@ -470,7 +473,23 @@ namespace simpleJson {
} }
void JsonNode::setValueType(simpleJson::JsonValueTypeEnum _type) { void JsonNode::setValueType(simpleJson::JsonValueTypeEnum _type) {
type = _type; type = _type;
}
bool JsonNode::as_bool() const {
switch (type) {
case JSON_TYPE_INT:
return static_cast<bool>(value.int_value);
case JSON_TYPE_DOUBLE:
return static_cast<bool>(value.double_value);
case JSON_TYPE_BOOL:
return value.bool_value;
case JSON_TYPE_NULL:
return false;
default:
throw JsonTypeError(INVALID_TYPE, __func__, JSON_TYPE_BOOL, type);
}
return false;
} }
/*****************************************************************************************************************/ /*****************************************************************************************************************/
@@ -501,7 +520,7 @@ namespace simpleJson {
} }
} }
JsonReader::JsonReader(std::ifstream & _input_file_stream) { JsonReader::JsonReader(std::ifstream &_input_file_stream) {
if (!_input_file_stream) { if (!_input_file_stream) {
throw std::runtime_error("JsonReader::JsonReader() error: input file stream is not valid"); throw std::runtime_error("JsonReader::JsonReader() error: input file stream is not valid");
} }
@@ -535,7 +554,6 @@ namespace simpleJson {
if (eof) { if (eof) {
throw std::runtime_error("JsonReader::get_char() error: end of file"); throw std::runtime_error("JsonReader::get_char() error: end of file");
} else { } else {
curr_ptr++;
if (curr_ptr >= src_str_len) { if (curr_ptr >= src_str_len) {
eof = 1; eof = 1;
return '\0'; return '\0';
@@ -582,7 +600,9 @@ namespace simpleJson {
} }
void JsonReader::skip_space() { void JsonReader::skip_space() {
while (curr_ptr < src_str_len && (src_str[curr_ptr] == ' ' || src_str[curr_ptr] == '\t' || src_str[curr_ptr] == '\n' || src_str[curr_ptr] == '\r')) { while (curr_ptr < src_str_len &&
(src_str[curr_ptr] == ' ' || src_str[curr_ptr] == '\t' || src_str[curr_ptr] == '\n' ||
src_str[curr_ptr] == '\r')) {
get_char(); get_char();
} }
if (curr_ptr >= src_str_len) { if (curr_ptr >= src_str_len) {
@@ -594,5 +614,202 @@ namespace simpleJson {
return eof; return eof;
} }
/****************************************************************************************************************/ /****************************************************************************************************************/
/**********************************************JsonParser********************************************************/
JsonParser::JsonParser() {
root = JsonNodePtr(new JsonNode(JSON_TYPE_OBJECT));
}
JsonParser::JsonParser(const std::string &_src_str) {
root = JsonNodePtr(new JsonNode(JSON_TYPE_OBJECT));
try {
JsonReader reader(_src_str);
parseValue(reader, root);
is_parse_success = true;
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
is_parse_success = false;
}
}
JsonParser::JsonParser(std::ifstream &_input_file_stream) {
root = JsonNodePtr(new JsonNode(JSON_TYPE_OBJECT));
try {
JsonReader reader(_input_file_stream);
parseValue(reader, root);
is_parse_success = true;
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
is_parse_success = false;
}
}
JsonNode &JsonParser::get_root() {
return *root;
}
bool JsonParser::is_success() const {
return is_parse_success;
}
bool JsonParser::fail() const {
return !is_parse_success;
}
void JsonParser::parseValue(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {
reader.skip_space();
char ch = reader.curr_char();
if (ch == '{') {
parseObject(reader, node);
} else if (ch == '\"') {
parseString(reader, node);
} else if (ch == '[') {
parseList(reader, node);
} else if (ch == '-' || isdigit(ch)) {
parseNumber(reader, node);
} else {
if (reader.str_ahead(4) == "true") {
node->setValueType(JSON_TYPE_BOOL);
node->set_value(1);
reader.get_str(4);
} else if (reader.str_ahead(5) == "false") {
node->setValueType(JSON_TYPE_BOOL);
node->set_value(0);
reader.get_str(5);
} else if (reader.str_ahead(4) == "null" || reader.str_ahead(4) == "NULL") {
node->setValueType(JSON_TYPE_NULL);
reader.get_str(4);
} else {
throw JsonParseError(reader.get_curr_line(), reader.get_curr_col(), SYNTAX_ERROR);
}
}
}
void JsonParser::parseObject(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {
node->setValueType(JSON_TYPE_OBJECT);
auto ch = reader.curr_char();
if (ch != '{') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
reader.skip_space();
ch = reader.curr_char();
}
while (ch != '}') {
if (ch != '\"') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
ch = reader.curr_char();
std::string key;
while (ch != '\"') {
key += reader.get_char();
ch = reader.curr_char();
}
reader.get_char();
reader.skip_space();
ch = reader.curr_char();
if (ch != ':') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
reader.skip_space();
ch = reader.curr_char();
JsonNodePtr child(new JsonNode(JSON_TYPE_NULL, key));
parseValue(reader, child);
node->push_back(child);
reader.skip_space();
ch = reader.curr_char();
if (ch == ',') {
reader.get_char();
reader.skip_space();
} else if (ch != '}') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
return;
}
}
}
ch = reader.curr_char();
}
reader.get_char();
}
void JsonParser::parseList(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {
node->setValueType(JSON_TYPE_LIST);
char ch = reader.curr_char();
if (ch != '[') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
reader.skip_space();
ch = reader.curr_char();
}
while (ch != ']') {
JsonNodePtr child(new JsonNode(JSON_TYPE_NULL));
parseValue(reader, child);
node->push_back(child);
reader.skip_space();
ch = reader.curr_char();
if (ch == ',') {
reader.get_char();
reader.skip_space();
} else if (ch != ']') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
} else {
reader.get_char();
return;
}
ch = reader.curr_char();
}
reader.get_char();
}
void JsonParser::parseString(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {
node->setValueType(JSON_TYPE_STRING);
std::string str;
char ch = reader.curr_char();
if (ch != '\"') {
throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch);
}
reader.get_char();
ch = reader.curr_char();
while (ch != '\"') {
if (ch == '\\') {
reader.get_char();
ch = reader.curr_char();
if (ch == '\"' || ch == '\\' || ch == '/' || ch == '\b' || ch == '\f' || ch == '\n' || ch == '\r' ||
ch == '\t') {
str += ch;
} else if (ch == 'u') {
//unicode TODO
} else {
throw JsonInvalidEscapedSym(reader.get_curr_line(), reader.get_curr_col(), ch);
}
reader.get_char();
ch = reader.curr_char();
} else {
str += ch;
reader.get_char();
ch = reader.curr_char();
}
}
reader.get_char();
node->set_value(str);
}
void JsonParser::parseNumber(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {}
/****************************************************************************************************************/
} }
+9
View File
@@ -29,6 +29,15 @@ namespace simpleJson {
JSON_TYPE_LIST, JSON_TYPE_LIST,
JSON_TYPE_OBJECT JSON_TYPE_OBJECT
}; };
const static std::string_view JSON_TYPE_STR[] = {
"JSON_TYPE_NULL",
"JSON_TYPE_INT",
"JSON_TYPE_DOUBLE",
"JSON_TYPE_BOOL",
"JSON_TYPE_STRING",
"JSON_TYPE_LIST",
"JSON_TYPE_OBJECT"
};
union JsonValue { union JsonValue {
int int_value; int int_value;
double double_value; double double_value;