From: Arjen Baart Date: Tue, 24 Mar 2020 06:21:13 +0000 (+0100) Subject: Add classes xml_node and xml_element X-Git-Url: http://www.andromeda.nl/gitweb/?a=commitdiff_plain;h=e9d51f5e41386315bf332a68b9f4e351361da98a;p=libacl.git Add classes xml_node and xml_element --- diff --git a/doc/Makefile.am b/doc/Makefile.am index e863ea0..cb8ac88 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -7,7 +7,7 @@ SUFFIXES = .obj .eps .png .obj.eps: tgif -print -eps -color $< -XMLS = manual.xml string.xml date.xml hour.xml utc.xml Integer.xml +XMLS = manual.xml string.xml date.xml hour.xml utc.xml Integer.xml xml.xml IMAGES= PICTURES= diff --git a/doc/manual.xml b/doc/manual.xml index 79bb2b9..32e8d08 100644 --- a/doc/manual.xml +++ b/doc/manual.xml @@ -15,5 +15,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude"/> + diff --git a/doc/xml.xml b/doc/xml.xml new file mode 100644 index 0000000..11d9e34 --- /dev/null +++ b/doc/xml.xml @@ -0,0 +1,100 @@ + +xml - XML document classes + +
+#include <xml.h>
+
+xml document;
+xml_node node;
+xml_element element;
+
+ + +The collection of xml classes provide an easy way to process XML documents +and to traverse the tree of XML data. +Built upon the xml2 library on xmlsoft.org, the classes encapsulate concepts +of XML data. +After parsing an XML document with an xml object, the xml_node class +can access the data of the XML document tree. +A node in the tree can have any number of children which are also nodes. +The child nodes are accessed by using the subscript operator with a numerical index. +Elements of the XML data can be selected by name when a String argument is used +with the subscript operator. +Using this operator will return a vector of xml_element objects that hold +all child elements having this name. +
+
+xml data(filename);
+xml_node root(data);
+xml_node third = root[2];
+
+String tagname("chapter");
+std::vector <xml_element> all_chapters;
+
+all_chapters = root[tagname];
+
+
+
+
+Class xml + +An object of class xml holds an entire XML document. +It supports reading and writing XML documents from and to files +as well conversion of XML objects to and from String objects. + + + +The default constructor creates an empty xml document. + + +Parse the xml document from s. + + + + + + +Parse the xml document from String s. + + + +
+ +
+class xml_node + + +A node is the basic building block that makes up the tree of an XML document. +There are several kinds of nodes, such as ekements, attributes and text nodes. +If a node is an ekement node it can have any number if child nodes. +These child nodes can be accessed by using the subscript operator with +a numerical index. + + + + +The default constructor creates an empty xml node. + + +Construct a node from the XML document. The xml_node object will be the root node +of the XML document. + + +Return true if the object actually is an XML node. + + +Return the child node with index n. + + +
+ +
+class xml_element : xml_node + +The class xml_element is derived from the class xml_node and adds properties +soecific to XML ekements. + + +
+ +
diff --git a/src/xml.cpp b/src/xml.cpp index 2ec74c7..3e9267a 100644 --- a/src/xml.cpp +++ b/src/xml.cpp @@ -1,13 +1,23 @@ #include "xml.h" +void xml::ParseFile(const char * filename) +{ + ctxt = xmlNewParserCtxt(); + //document = xmlParseFile(filename); + document = xmlCtxtReadFile(ctxt, filename, NULL, XML_PARSE_NOWARNING|XML_PARSE_NOERROR); +} + String xml::RootElementName(void) { String root_name; xmlNodePtr root; - root = xmlDocGetRootElement(document); - root_name = String((const char *)root->name); + if (document != NULL) + { + root = xmlDocGetRootElement(document); + root_name = String((const char *)root->name); + } return root_name; } @@ -21,7 +31,25 @@ xml_node xml::RootNode(void) return xml_node(root); } +//xml_node::xml_node(xml doc) +//{ +// *this = doc.RootNode(); +//} + // Find the nth child element by name (non-recursive) +xml_node xml_node::operator[](int i) +{ + xmlNodePtr np = n->children; + + while (np != NULL && i != 0) + { + i--; + np = np->next; + } + + return xml_node(np); +} + xml_node xml_node::FindChildElement(String name, int nth) { xml_node element; @@ -42,3 +70,22 @@ xml_node xml_node::FindChildElement(String name, int nth) return element; } + + +std::vector xml_element::operator[](String tagname) +{ + std::vector elements_found; + + xmlNodePtr np = n->children; + while (np != NULL) + { + if (np->type == XML_ELEMENT_NODE && tagname == (const char *)np->name) + { + xml_element e(np); + elements_found.push_back(e); + } + np = np->next; + } + + return elements_found; +} diff --git a/src/xml.h b/src/xml.h index 63f1e5a..4579a17 100644 --- a/src/xml.h +++ b/src/xml.h @@ -1,9 +1,47 @@ #include // usually in /usr/include/libxml2, see xml2-config +#include #include "String.h" +class xml_node; + +class xml +{ + xmlParserCtxtPtr ctxt; + xmlDocPtr document; + +public: + + xml() + { + ctxt = NULL; + document = NULL; + } + + ~xml() + { + if (document != NULL) + { + xmlFreeDoc(document); + document = NULL; + } + } + + void ParseFile(const char * filename); + + void SaveFile(const char * filename) + { + xmlSaveFile(filename, document); + } + + String RootElementName(void); + xml_node RootNode(void); + +}; + class xml_node { +protected: xmlNodePtr n; public: @@ -18,11 +56,21 @@ public: n = np; } + xml_node(xml &doc) + { + xml_node node; + node = doc.RootNode(); + + n = node.n; + } + bool is_a_node(void) { return n != NULL; } + xml_node operator[](int n); + String name(void) { String node_name; @@ -35,6 +83,19 @@ public: return node_name; } + xmlElementType type(void) + { + xmlElementType nodetype; + + nodetype = xmlElementType(0); + if (n != NULL) + { + nodetype = n->type; + } + + return nodetype; + } + // Assumes one childnode of type TEXT // TODO collect content from multiple TEXT nodes (recursively ?) String content(void) @@ -53,38 +114,22 @@ public: xml_node FindChildElement(String name, int nth = 0); }; -class xml +class xml_element : public xml_node { - xmlDocPtr document; - public: - - xml() + xml_element() : xml_node() { - document = NULL; } - ~xml() + xml_element(xmlNodePtr np) : xml_node(np) { - if (document != NULL) - { - xmlFreeDoc(document); - document = NULL; - } } - void ParseFile(const char * filename) + xml_element(xml &doc) : xml_node(doc) { - document = xmlParseFile(filename); } - void SaveFile(const char * filename) - { - xmlSaveFile(filename, document); - } + std::vector operator[](String tagname); - String RootElementName(void); - xml_node RootNode(void); - }; diff --git a/test/Makefile.am b/test/Makefile.am index 740bac0..f593b49 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -7,7 +7,7 @@ check_PROGRAMS = string_assign string_basics string_compare string_cat string_su hour_assign hour_parse hour_compare hour_arithmetic hour_check_now \ utc_assign utc_compare utc_arithmetic \ integer_assign integer_factorial integer_fibonacci integer_pow64 integer_modulo \ - xml_file xml_find \ + xml_file xml_find xml_node xml_elem \ configuration_read configuration_find string_assign_SOURCES = string_assign.cpp @@ -46,6 +46,8 @@ integer_modulo_SOURCES = integer_modulo.cpp tInteger.cpp xml_file_SOURCES = xml_file.cpp xml_find_SOURCES = xml_find.cpp +xml_node_SOURCES = xml_node.cpp +xml_elem_SOURCES = xml_elem.cpp configuration_read_SOURCES = configuration_read.cpp configuration_find_SOURCES = configuration_find.cpp diff --git a/test/xml_elem.cpp b/test/xml_elem.cpp new file mode 100644 index 0000000..02a9b0d --- /dev/null +++ b/test/xml_elem.cpp @@ -0,0 +1,59 @@ +/******************************************************* + * Unit test for the xml_element class + * + * test xml element access + ****************************************************** + * + */ + +#include "xml.h" +#include + +int main() +{ + xml doc; + + const char xml_file[] = "xml_test01.xml"; + + std::cout << "Reading XML file " << xml_file << "\n"; + + doc.ParseFile("xml_test01.xml"); + + xml_element root_node(doc); + + std::cout << "Root element is " << root_node.name() << "\n"; + std::cout << "Root node type is " << root_node.type() << "\n"; + assert(root_node.name() == "doc"); + + std::vector book, chapters; + + book = root_node["book"]; + assert(book.size() == 1); + + chapters = book[0]["chapter"]; + + std::cout << "Found " << chapters.size() << " chapter elements.\n"; + + std::vector report; + + report = root_node["report"]; + assert(report.size() == 0); + + // Find an element three levels deep + + std::vector children; + xml_element found_node; + + children = book[0]["titlepage"]; + found_node = children[0]; + + assert(found_node.is_a_node() == true); + children = found_node["title"]; + found_node = children[0]; + assert(found_node.is_a_node() == true); + std::cout << "Name of title node = " << found_node.name() << "\n"; + std::cout << "Content of title node = " << found_node.content() << "\n"; + + return 0; +} + diff --git a/test/xml_elem.exp b/test/xml_elem.exp new file mode 100644 index 0000000..0517b13 --- /dev/null +++ b/test/xml_elem.exp @@ -0,0 +1,7 @@ +Reading XML file xml_test01.xml +Root element is doc +Root node type is 1 +Found 3 chapter elements. +Name of title node = title +Content of title node = Andromeda Class Library +PASS xml_elem (exit status: 0) diff --git a/test/xml_file.cpp b/test/xml_file.cpp index ea4722e..6e9adcd 100644 --- a/test/xml_file.cpp +++ b/test/xml_file.cpp @@ -22,8 +22,19 @@ int main() assert (root == "doc"); root_node = doc.RootNode(); + assert(root_node.is_a_node() == true); assert(root_node.name() == "doc"); + doc.ParseFile("notexist.xml"); + root_node = doc.RootNode(); + assert(root_node.is_a_node() == false); + assert(root_node.name() == ""); + + doc.ParseFile("xml_test02.xml"); + root_node = doc.RootNode(); + assert(root_node.is_a_node() == false); + assert(root_node.name() == ""); + return 0; } diff --git a/test/xml_find.cpp b/test/xml_find.cpp index b5c3cd6..8bebaa9 100644 --- a/test/xml_find.cpp +++ b/test/xml_find.cpp @@ -25,9 +25,6 @@ int main() root_node = doc.RootNode(); - std::cout << "Root element is " << root_node.name() << "\n"; - assert(root_node.name() == "doc"); - child_name = "book"; found_node = root_node.FindChildElement(child_name); std::cout << "Child element " << child_name; diff --git a/test/xml_find.exp b/test/xml_find.exp index f62e36b..db39d6d 100644 --- a/test/xml_find.exp +++ b/test/xml_find.exp @@ -1,5 +1,4 @@ Reading XML file xml_test01.xml -Root element is doc Child element book found. Child element report not found. Name of title node = title diff --git a/test/xml_node.cpp b/test/xml_node.cpp new file mode 100644 index 0000000..e45b480 --- /dev/null +++ b/test/xml_node.cpp @@ -0,0 +1,42 @@ +/******************************************************* + * Unit test for the xml_node class + * + * test xml node access + ****************************************************** + * + */ + +#include "xml.h" +#include + +int main() +{ + xml doc; + + const char xml_file[] = "xml_test01.xml"; + + std::cout << "Reading XML file " << xml_file << "\n"; + + doc.ParseFile("xml_test01.xml"); + + xml_node root_node(doc); + + std::cout << "Root element is " << root_node.name() << "\n"; + std::cout << "Root node type is " << root_node.type() << "\n"; + assert(root_node.name() == "doc"); + + int i; + xml_node child; + + root_node = root_node[1]; // The book element + child = root_node[0]; + for (i = 0; child.is_a_node(); i++) + { + child = root_node[i]; + std::cout << "Node " << i << " type is " << child.type() << "\n"; + std::cout << "Node " << i << " name is " << child.name() << "\n"; + } + + return 0; +} + diff --git a/test/xml_test01.xml b/test/xml_test01.xml index a6dd12d..dcfd8d3 100644 --- a/test/xml_test01.xml +++ b/test/xml_test01.xml @@ -1,6 +1,6 @@ - + Andromeda Class Library diff --git a/test/xml_test02.xml b/test/xml_test02.xml new file mode 100644 index 0000000..1a0069a --- /dev/null +++ b/test/xml_test02.xml @@ -0,0 +1,3 @@ +