/
volodja
/
iridium
Обзор
Документация
Войти
/
volodja
/
iridium
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
source/test/parsing/parsing.cpp
508 строк
16 KB
volodja
- parsing: json parser optimisation
04 авг 2026, 20:05
04 авг 2026, 20:05
57d8a5d
Код
Авторство
О чём код?
#include <iridium/testing/tester.h> #include <iridium/parsing/node.h> #include <iridium/parsing/implementation/node.h> #include <iridium/parsing/parser.h> #include <iridium/parsing/implementation/xml_parser.h> #include <iridium/parsing/implementation/json_parser.h> #include <iridium/parsing/implementation/http_parser.h> #include <iridium/assert.h> #include <iostream> using namespace std; using std::string; using iridium::parsing::INode; using iridium::parsing::implementation::CNode; using iridium::parsing::implementation::CXMLParser; using iridium::parsing::implementation::CJSONParser; using iridium::parsing::implementation::CHTTPParser; namespace { string const beer_xml = "" "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<MyBeerJournal>\n" " <Brewery location=\"Grand Rapids, MI\" name=\"Founders Brewing Company\">\n" " <Beer dateSampled=\"01/02/2011\" description=\"IPA\" name=\"Centennial\" rating=\"A+\">\n" " \"What an excellent IPA. This is the most delicious beer I have ever tasted!\"\n" " </Beer>\n" " </Brewery>\n" " <Brewery location=\"Grand Rapids, MI\" name=\"Brewery Vivant\">\n" " <Beer dateSampled=\"02/07/2015\" description=\"Belgian Ale\" name=\"Farmhouse Ale\" rating=\"B\">\n" " This beer is not so good... but I am not that big of a fan of english style ales.\n" " </Beer>\n" " </Brewery>\n" " <Brewery location=\"Kalamazoo, MI\" name=\"Bells Brewery\">\n" " <Beer dateSampled=\"03/15/2012\" description=\"IPA\" name=\"Two Hearted Ale\" rating=\"A\">\n" " Another execllent brew. Two Hearted gives Founders Centennial a run for it's money.\n" " </Beer>\n" " </Brewery>\n" " <array>5</array>\n" " <array>4</array>\n" " <array>3</array>\n" " <array>2</array>\n" " <array>1</array>\n" "</MyBeerJournal>\n\n"; string const beer_json = "" "{\n" " \"MyBeerJournal\": {\n" " \"Brewery\": [\n" " {\n" " \"Beer\": {\n" " \"#text\": \"\n \\\"What an excellent IPA. This is the most delicious beer I have ever tasted!\\\"\n \",\n" " \"dateSampled\": \"01/02/2011\",\n" " \"description\": \"IPA\",\n" " \"name\": \"Centennial\",\n" " \"rating\": \"A+\"\n" " },\n" " \"location\": \"Grand Rapids, MI\",\n" " \"name\": \"Founders Brewing Company\"\n" " },\n" " {\n" " \"Beer\": {\n" " \"#text\": \"\n This beer is not so good... but I am not that big of a fan of english style ales.\n \",\n" " \"dateSampled\": \"02/07/2015\",\n" " \"description\": \"Belgian Ale\",\n" " \"name\": \"Farmhouse Ale\",\n" " \"rating\": \"B\"\n" " },\n" " \"location\": \"Grand Rapids, MI\",\n" " \"name\": \"Brewery Vivant\"\n" " },\n" " {\n" " \"Beer\": {\n" " \"#text\": \"\n Another execllent brew. Two Hearted gives Founders Centennial a run for it's money.\n \",\n" " \"dateSampled\": \"03/15/2012\",\n" " \"description\": \"IPA\",\n" " \"name\": \"Two Hearted Ale\",\n" " \"rating\": \"A\"\n" " },\n" " \"location\": \"Kalamazoo, MI\",\n" " \"name\": \"Bells Brewery\"\n" " }\n" " ],\n" " \"array\": [\n" " \"5\",\n" " \"4\",\n" " \"3\",\n" " \"2\",\n" " \"1\"\n" " ]\n" " }\n" "}\n"; INode::TSharedPtr createTestNode() { auto root_node = CNode::create("root"); { auto item = root_node->addChild("item"); item->addChild("attr_value", "value_1"); item->addChild("attr_name", "name_1"); } { auto item = root_node->addChild("item"); item->addChild("attr_value", "value_2"); item->addChild("attr_name", "name_2"); auto sub_item = item->addChild("sub_item"); sub_item->addChild("item", "sub_item_attr_value_5"); } { root_node->addChild("array", "5"); root_node->addChild("array", "4"); root_node->addChild("array", "3"); root_node->addChild("array", "2"); root_node->addChild("array", "1"); } { auto item = root_node->addChild("item"); item->addChild("", "text"); } return root_node; } string const xml_str_expects = "" "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<root>\n" " <item attr_value=\"value_1\" attr_name=\"name_1\"/>\n" " <item attr_value=\"value_2\" attr_name=\"name_2\">\n" " <sub_item item=\"sub_item_attr_value_5\"/>\n" " </item>\n" " <array>5</array>\n" " <array>4</array>\n" " <array>3</array>\n" " <array>2</array>\n" " <array>1</array>\n" " <item>text</item>\n" "</root>\n\n"; string const json_str_expects = "" "{\n" " \"root\": {\n" " \"item\": [\n" " {\n" " \"attr_value\": \"value_1\",\n" " \"attr_name\": \"name_1\"\n" " },\n" " {\n" " \"attr_value\": \"value_2\",\n" " \"attr_name\": \"name_2\",\n" " \"sub_item\": {\n" " \"item\": \"sub_item_attr_value_5\"\n" " }\n" " },\n" " {\n" " \"#text\": \"text\"\n" " }\n" " ],\n" " \"array\": [\n" " \"5\",\n" " \"4\",\n" " \"3\",\n" " \"2\",\n" " \"1\"\n" " ]\n" " }\n" "}\n"; string const http_header = "" "GET / HTTP/1.1\n" "content-type: text/html; charset=windows-1251\n" "Allow: GET, HEAD\n" "Content-Length: 356\n" "ALLOW: GET, OPTIONS\n" "Content-Length: 1984"; string const http_header_node = "\n" "'http' = ''\n" " 'message' = 'GET / HTTP/1.1'\n" " 'headers' = ''\n" " 'content-type' = 'text/html; charset=windows-1251'\n" " 'allow' = 'GET'\n" " 'allow' = 'HEAD'\n" " 'allow' = 'OPTIONS'\n" " 'content-length' = '1984'\n" " 'body' = ''"; string const http_header_composed = "" "GET / HTTP/1.1\n" "content-type: text/html; charset=windows-1251\n" "allow: GET, HEAD, OPTIONS\n" "content-length: 1984\n\n"; // 2026-07-20 08:50:46.469 T 0x1fa359d80 parsing.cpp:255 // 'MyBeerJournal' = '' // 'Brewery' = '' // 'Beer' = '' // '' = ' // "What an excellent IPA. This is the most delicious beer I have ever tasted!" // ' // 'dateSampled' = '01/02/2011' // 'description' = 'IPA' // 'name' = 'Centennial' // 'rating' = 'A+' // 'location' = 'Grand Rapids, MI' // 'name' = 'Founders Brewing Company' // 'Brewery' = '' // 'Beer' = '' // '' = ' // This beer is not so good... but I am not that big of a fan of english style ales. // ' // 'dateSampled' = '02/07/2015' // 'description' = 'Belgian Ale' // 'name' = 'Farmhouse Ale' // 'rating' = 'B' // 'location' = 'Grand Rapids, MI' // 'name' = 'Brewery Vivant' // 'Brewery' = '' // 'Beer' = '' // '' = ' // Another execllent brew. Two Hearted gives Founders Centennial a run for it's money. // ' // 'dateSampled' = '03/15/2012' // 'description' = 'IPA' // 'name' = 'Two Hearted Ale' // 'rating' = 'A' // 'location' = 'Kalamazoo, MI' // 'name' = 'Bells Brewery' // 'array' = '5' // 'array' = '4' // 'array' = '3' // 'array' = '2' // 'array' = '1' // 2026-07-20 08:50:46.470 I 0x1fa359d80 OK /parsing/parsing.cpp/parse_json } // unnamed namespace iridium::parsing { TEST(parse_xml) { auto parser = CXMLParser::create(); auto node = parser->parse(beer_xml); ASSERT(node->hasChilds()); ASSERT("MyBeerJournal", equal, node->getName()); auto value_nodes = assertComplete(node->slice("/Brewery/Beer/name"), "slice is empty"); ASSERT( 3 , equal, value_nodes.size()); ASSERT("Centennial" , equal, std::next(value_nodes.begin(), 0)->get()->getValue()); ASSERT("Farmhouse Ale" , equal, std::next(value_nodes.begin(), 1)->get()->getValue()); ASSERT("Two Hearted Ale", equal, std::next(value_nodes.begin(), 2)->get()->getValue()); string array; for (auto const &i: *node) if (i->getName() == "array") array += i->getValue(); ASSERT("54321", equal, array); } TEST(compose_xml) { auto parser = CXMLParser::create(); auto node = createTestNode(); auto xml_str = parser->compose(node); ASSERT(xml_str_expects, equal, xml_str); } TEST(parse_json_basic) { auto parser = CJSONParser::create(); { auto node = parser->parse("{}"); ASSERT("root", equal, node->getName()); ASSERT("", equal, node->getValue()); } { auto node = parser->parse("{ \"key\": \"value\" }"); ASSERT("key", equal, node->getName()); ASSERT("value", equal, node->getValue()); } { auto node = parser->parse("{ \"key1\":\"null\n\"} "); ASSERT("key1", equal, node->getName()); ASSERT("null\n", equal, node->getValue()); } { auto node = parser->parse("{ \"key2\":123.0}"); ASSERT("key2", equal, node->getName()); ASSERT("123.0", equal, node->getValue()); } { auto node = parser->parse("{ \"parent\":{\"key3\":1},\"key4\":2}"); ASSERT(static_cast<bool>(node->getChild("parent"))); ASSERT(static_cast<bool>(node->getChild("parent")->getChild("key3"))); ASSERT(static_cast<bool>(node->getChild("key4"))); ASSERT("1", equal, node->getChild("parent")->getChild("key3")->getValue()); ASSERT("2", equal, node->getChild("key4")->getValue()); } } TEST(parse_json_beer_journal) { auto parser = CJSONParser::create(); auto node = parser->parse(beer_json); ASSERT(8, equal, node->size()); ASSERT("MyBeerJournal", equal, node->getName()); auto value_nodes = assertComplete(node->slice("/Brewery/Beer/name"), "slice is empty"); ASSERT( 3 , equal, value_nodes.size()); ASSERT("Centennial" , equal, std::next(value_nodes.begin(), 0)->get()->getValue()); ASSERT("Farmhouse Ale" , equal, std::next(value_nodes.begin(), 1)->get()->getValue()); ASSERT("Two Hearted Ale", equal, std::next(value_nodes.begin(), 2)->get()->getValue()); string array; for (auto const &i: *node) if (i->getName() == "array") array += i->getValue(); ASSERT("54321" , equal, array); } TEST(parse_json_unquoted_values) { auto parser = CJSONParser::create(); ASSERT("true" , equal, parser->parse("{ \"value\":true }")->getValue()); ASSERT("false" , equal, parser->parse("{ \"value\":false }")->getValue()); ASSERT("null" , equal, parser->parse("{ \"value\":null }")->getValue()); ASSERT("0.123456789" , equal, parser->parse("{ \"value\":0.123456789 }")->getValue()); ASSERT("null0" , equal, parser->parse("{ \"value\":\"null0\" }")->getValue()); ASSERT(parser->parse("{ \"value\":null0 }"), std::exception); } TEST(parse_json_arrays) { auto parser = CJSONParser::create(); { auto slice = parser->parse("{ \"array\": [ {\"value\": 1}, {\"value\": 2} ] }")->slice("/array/value"); ASSERT( 2 , equal, slice.size()); ASSERT("value" , equal, slice.front()->getName()); ASSERT("1" , equal, slice.front()->getValue()); ASSERT("value" , equal, slice.back()->getName()); ASSERT("2" , equal, slice.back()->getValue()); } { auto slice = parser->parse("{ \"value\": [ \"1\", \"2\" ] }")->slice("/value"); ASSERT( 2 , equal, slice.size()); ASSERT("value" , equal, slice.front()->getName()); ASSERT("1" , equal, slice.front()->getValue()); ASSERT("value" , equal, slice.back()->getName()); ASSERT("2" , equal, slice.back()->getValue()); } { auto slice = parser->parse("[ {\"value\": 1}, {\"value\": 2} ]")->slice("/array/value"); ASSERT( 2 , equal, slice.size()); ASSERT("value" , equal, slice.front()->getName()); ASSERT("1" , equal, slice.front()->getValue()); ASSERT("value" , equal, slice.back()->getName()); ASSERT("2" , equal, slice.back()->getValue()); } { auto slice = parser->parse("[ \"1\", \"2\" ]")->slice("/array"); ASSERT( 2 , equal, slice.size()); ASSERT("array" , equal, slice.front()->getName()); ASSERT("1" , equal, slice.front()->getValue()); ASSERT("array" , equal, slice.back()->getName()); ASSERT("2" , equal, slice.back()->getValue()); } { auto slice = parser->parse("[ 1, 2 ]")->slice("/array"); ASSERT( 2 , equal, slice.size()); ASSERT("array" , equal, slice.front()->getName()); ASSERT("1" , equal, slice.front()->getValue()); ASSERT("array" , equal, slice.back()->getName()); ASSERT("2" , equal, slice.back()->getValue()); } } TEST(parse_json_syntax_errors) { auto parser = CJSONParser::create(); ASSERT(parser->parse("[ {\"value\": 1}, {\"value\": 2 ]"), std::exception); ASSERT(parser->parse("[ {\"value\": 1}, {\"value\": 2 }"), std::exception); ASSERT(parser->parse("[ {\"value\": 1}, {\"value\": 2"), std::exception); ASSERT(parser->parse("[ {\"value\": 1, {\"value\": 2 }]"),std::exception); // ASSERT(parser->parse("{\"value\" 1 }"), std::exception); // LOGT << "BEGIN"; ASSERT(parser->parse("{\"value\": 1,}"), std::exception); // LOGT << "END"; } TEST(parse_json_edge_cases) { auto parser = CJSONParser::create(); INode::TSharedPtr node; node = parser->parse( "{\n" " \"result\": {\n" " \"tests\": [\n" " {\n" " \"path\": \"/parsing/serialization/http.cpp/http_request\",\n" " \"error\": \"\",\n" " \"output\": \"\"\n" " },\n" " {\n" " \"path\": \"/parsing/serialization/http.cpp/http_response\",\n" " \"error\": \"\",\n" " \"output\": \"\"\n" " }\n" " ]\n" " }\n" "}\n"); node = parser->parse( "{ \"value\":" "2110\t" "}"); } TEST(compose_json) { auto parser = CJSONParser::create(); auto node = createTestNode(); auto json_str = parser->compose(node); ASSERT(json_str_expects, equal, json_str); } // todo: differ in windows and linux //TEST(parsing, parse_http_request) { // auto parser = CHTTPParser::create(); // auto node = parser->parse(http_header); // // ASSERT_EQ(http_header_node, convertion::convert<string>(node)); //} TEST(compose_http_request) { auto parser = CHTTPParser::create(); auto node = parser->parse(http_header); auto http_header_ = parser->compose(node); ASSERT(http_header_composed, equal, http_header_); } TEST(parse_compose) { auto json_parser = CJSONParser::create(); auto xml_parser = CXMLParser::create(); auto node = json_parser->parse(beer_json); auto xml_str = xml_parser->compose(node); ASSERT(beer_xml, equal, xml_str); auto json_str = json_parser->compose(node); ASSERT(beer_json, equal, json_str); } } // namespace iridium::parsing