From bbacaee3ae471d3159f0d3e73ce9a36c4a521d3a Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Wed, 29 Aug 2018 17:07:33 +0800 Subject: [PATCH 1/9] =?UTF-8?q?fix=20the=20include=20headfile=20style=20=20=3D=E3=80=8B"tixml2ex.h"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tixml2cx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tixml2cx.h b/tixml2cx.h index 492756b..e6183e6 100644 --- a/tixml2cx.h +++ b/tixml2cx.h @@ -30,7 +30,7 @@ It can be found here: https://github.com/leethomason/tinyxml2 and has it's own l #include #ifndef __TINYXML_EX__ -#include +#include "tixml2ex.h" #endif // !__TINYXML_EX__ namespace tinyxml2 From aa5603e33132273e9d0f6be99466512211dfb5d0 Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Wed, 29 Aug 2018 17:19:12 +0800 Subject: [PATCH 2/9] fix the iterator== error when _selectionPath is empty --- tixml2ex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tixml2ex.h b/tixml2ex.h index 9791722..81bf80e 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -246,7 +246,7 @@ namespace tinyxml2 _selectionPath .pop_front(); } XE * operator *() const { return !_selectionPath .empty() ? _selectionPath .back() .second : nullptr; } - bool operator == (const ElementIterator & iter) const { return *iter == _selectionPath .back() .second; } + bool operator == (const ElementIterator & iter) const { return iter.operator*() == this->operator*(); } bool operator != (const ElementIterator & iter) const { return ! operator == (iter); } ElementIterator & operator ++() { From 506a01cee3926fb139f4b20e7db910d85a10978e Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Fri, 7 Sep 2018 17:45:54 +0800 Subject: [PATCH 3/9] fix the problem :can not search singal element whice is not root --- tixml2cx.h | 4 ++-- tixml2ex.h | 48 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/tixml2cx.h b/tixml2cx.h index e6183e6..006f5c8 100644 --- a/tixml2cx.h +++ b/tixml2cx.h @@ -37,7 +37,7 @@ namespace tinyxml2 { inline namespace tixml2ex { - class XMLCopy : public XMLVisitor + class TINYXML2_LIB XMLCopy : public XMLVisitor { public: XMLCopy (XMLElement * target) : _target(target) { _newDoc = target->GetDocument(); } @@ -88,7 +88,7 @@ namespace tinyxml2 }; // XMLCopy - class XMLCopyAndReplace : public XMLCopy + class TINYXML2_LIB XMLCopyAndReplace : public XMLCopy { public: XMLCopyAndReplace (XMLElement * target, const std::unordered_map & params, char openDelim, char closeDelim) diff --git a/tixml2ex.h b/tixml2ex.h index 81bf80e..717146f 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -1,4 +1,4 @@ -/* +/* tinyxml2ex - a set of add-on classes and helper functions bringing C++11/14 features, such as iterators, strings and exceptions, to tinyxml2 @@ -40,14 +40,14 @@ namespace tinyxml2 { using namespace std::literals::string_literals; - class XmlException : public std::logic_error + class TINYXML2_LIB XmlException : public std::logic_error { public: XmlException (const std::string & description) : logic_error (description) {} }; - class AttributeNameValue + class TINYXML2_LIB AttributeNameValue { public: AttributeNameValue (std::string name, std::string value) : _name(name), _value(value) {} @@ -62,7 +62,7 @@ namespace tinyxml2 using attribute_list_t = std::list ; - class ElementProperties + class TINYXML2_LIB ElementProperties { public: ElementProperties (std::string xProps) @@ -132,7 +132,7 @@ namespace tinyxml2 } ElementProperties() {} // an empty property set - std::string Name() const { return _name; } + const std::string& Name() const { return _name; } bool Match (const XMLElement * element) const { // n.b. we only match attributes here, not the element name (type) @@ -269,10 +269,46 @@ namespace tinyxml2 return false; auto parentElement = ixSel -> second; + bool isBegin = ixSel == _selectionPath.begin(); + if (++ixSel == _selectionPath .end()) return true; // we've found the first matching element - ixSel -> second = parentElement -> FirstChildElement (ixSel -> first .Name() .empty() ? nullptr : ixSel -> first .Name() .c_str()); + ixSel->second = parentElement->FirstChildElement(ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str()); + + //判断是否双斜杠,若不是跟目录,则寻找下一级 + if (isBegin && ixSel->second ==nullptr) + { + std::vector searchEleList = { parentElement }; + const char* eleName = ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str(); + + auto lastEle = searchEleList.back(); + while (searchEleList.size()) + { + auto& lastEle = searchEleList.back(); + if (auto ele = lastEle->FirstChildElement(eleName)) + { + ixSel->second = ele; + break; + } + else if(auto ele = lastEle->FirstChildElement()) + { + searchEleList.push_back(ele); + continue; + } + + //至少要next一次 + if (!(lastEle = lastEle->NextSiblingElement())) + { + while (searchEleList.back() == nullptr && searchEleList.size()) + { + searchEleList.pop_back(); + if (searchEleList.empty()) break; + searchEleList.back() = searchEleList.back()->NextSiblingElement(); + } + } + } + } while (ixSel -> second) { if (ixSel -> first .Match (ixSel -> second)) From 26d843370aeee6292d07f2b8cfa03bfc46074c06 Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Tue, 6 Nov 2018 14:40:28 +0800 Subject: [PATCH 4/9] add touch family :get or create an ElementI fix the xpath logic error that only find element begin at rootElenent --- tixml2ex.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tixml2ex.h b/tixml2ex.h index 717146f..50ab3b2 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -276,7 +276,7 @@ namespace tinyxml2 ixSel->second = parentElement->FirstChildElement(ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str()); - //判断是否双斜杠,若不是跟目录,则寻找下一级 + //判断是否双斜杠,若不是根目录,则寻找下一级 if (isBegin && ixSel->second ==nullptr) { std::vector searchEleList = { parentElement }; @@ -603,6 +603,15 @@ namespace tinyxml2 return append_element (parent, xpath, attributes, text, false); } + //touch family + inline XMLElement * touch_element(XMLElement * parent, const std::string & xpath, const attribute_list_t & attributes = {}, const std::string & text = "") + { + auto ele = find_element(parent, xpath); + if(ele==nullptr) ele = append_element(parent, xpath, attributes, text, true); + assert(ele); + return ele; + } + inline XMLElement * insert_next_element (XMLElement * sibling, const std::string & name, const attribute_list_t & attributes = {}, const std::string & text = ""s) { From 76a7f011db693d8814f3af36d99a336e8e0c502d Mon Sep 17 00:00:00 2001 From: ooklasd <631550367@qq.com> Date: Thu, 8 Nov 2018 09:28:49 +0800 Subject: [PATCH 5/9] chang the Chinese to English --- tixml2ex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tixml2ex.h b/tixml2ex.h index 50ab3b2..2d38068 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -276,7 +276,7 @@ namespace tinyxml2 ixSel->second = parentElement->FirstChildElement(ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str()); - //判断是否双斜杠,若不是根目录,则寻找下一级 + //To determine whether a double slash, if not the root directory, looks for the next level. if (isBegin && ixSel->second ==nullptr) { std::vector searchEleList = { parentElement }; @@ -297,7 +297,7 @@ namespace tinyxml2 continue; } - //至少要next一次 + //Next at least once. if (!(lastEle = lastEle->NextSiblingElement())) { while (searchEleList.back() == nullptr && searchEleList.size()) From bdee85c6e5b79a633dc992c928d95db79d284591 Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Fri, 16 Nov 2018 17:39:09 +0800 Subject: [PATCH 6/9] add xpath search model like A//B A/*/B --- .gitmodules | 3 + test/main.cpp | 70 ++++++++++- tinyxml2 | 1 + tixml2ex.h | 338 ++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 321 insertions(+), 91 deletions(-) create mode 100644 .gitmodules create mode 160000 tinyxml2 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2c941dc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tinyxml2"] + path = tinyxml2 + url = https://github.com/leethomason/tinyxml2.git diff --git a/test/main.cpp b/test/main.cpp index 1c6b014..b783e57 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,4 +1,4 @@ -/* +/* tinyxml2ex - a set of add-on classes and helper functions bringing C++11/14 features, such as iterators, strings and exceptions, to tinyxml2 @@ -30,6 +30,8 @@ It can be found here: https://github.com/leethomason/tinyxml2 and has it's own l // include the header for tinyxml2ex which includes tinyxml2, remember to put them on your include path #include +#include +#include using namespace std; using namespace std::literals::string_literals; @@ -95,10 +97,39 @@ int main() else printf ("unable to load XML document\n"); } + + std::map xpathElementCount = { + { R"(A)",1 } + ,{ R"(A/B/C)",3 } + ,{ R"(A//B/C)",3 } + ,{ R"(A/B//C)",3 } + ,{ R"(A//B//C)",3 } + ,{ R"(/A//B//C)",3 } + ,{R"(//A//B//C)",3} + ,{ R"(A/B[@code='1'])",2 } + ,{ R"(A/B[@code='1']/C)",3 } + }; + + for (auto& it : xpathElementCount) + { + auto element_path = tinyxml2::element_path_from_xpath(doc.RootElement(), it.first); + + auto& xpath = it.first; + auto expectCount = it.second; + //if it's not root + if (!(xpath.size() >= 2 && xpath[0] == '/' && xpath[1] != '/')) + { + expectCount++; + } + + assert(element_path.size() == expectCount); + } } cout << "----" << endl << endl; + + // 2) tixml2ex XPath selector : 10 lines of code cout << "2) element children of element children of the document element " << endl << "tixml2ex XPath selector" << endl; @@ -108,6 +139,43 @@ int main() // n.b. the static_cast makes the XMLDocument const and hence all XMLElements returned are also const for (auto eC : tinyxml2::selection (static_cast (*doc), "A/B/C")) cout << eC -> Name() << " = " << text (eC) << endl; + + cout << "=================================================" << endl << endl; + + size_t count = 0; + + for (auto eC : tinyxml2::selection(static_cast (*doc), "A//C")) + { + ++count; + } + assert(count== 5); + if (count != 5) + { + cout << "A//C expect 5 but actrul equal to "<RootElement(),"*/B/C"s)) + { + ++count; + } + assert(count == 0); + + count = 0; + for (auto it : tinyxml2::selection(doc->RootElement(), "*/C"s)) + { + ++count; + } + assert(count == 5); + + count = 0; + for (auto it : tinyxml2::selection(doc->RootElement(), "/A/*/C"s)) + { + ++count; + } + assert(count == 5); + + cout << "=================================================" << endl << endl; } catch (tinyxml2::XmlException & e) { diff --git a/tinyxml2 b/tinyxml2 new file mode 160000 index 0000000..44ac395 --- /dev/null +++ b/tinyxml2 @@ -0,0 +1 @@ +Subproject commit 44ac3951426359a34ccbd252e82f26e3448d5fd4 diff --git a/tixml2ex.h b/tixml2ex.h index 50ab3b2..ee79b19 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -65,7 +65,11 @@ namespace tinyxml2 class TINYXML2_LIB ElementProperties { public: - ElementProperties (std::string xProps) + enum class Location { + locationChildren, locationChildrenNoName, locationAllChildren, locationMyself, locationParent, locationFunction, locationRoot + }; + + ElementProperties (std::string xProps,size_t pos) { // parse xProps for element name and attribute filters using simplified XPath syntax std::string attributeName, attributeValue; @@ -104,12 +108,53 @@ namespace tinyxml2 else if (c == '\'') { if (!(_state == ParseState::attributeAssignment || _state == ParseState::attributeValue)) - throw XmlException ("ill formed XPath"s); + throw XmlException("ill formed XPath"s); // XPath attribute values are wrapped in single quote marks // but we don't require them and ignore them if present // toggle between two effectively equivalent modes _state = _state == ParseState::attributeAssignment ? ParseState::attributeValue : ParseState::attributeAssignment; } + else if (c == '.') + { + if (_state != ParseState::elementName) + throw XmlException("ill formed XPath"s); + + if (_location == Location::locationChildren) + { + _location = Location::locationMyself; + } + else if (_location == Location::locationMyself) + { + _location = Location::locationParent; + } + else + { + throw XmlException("ill formed XPath"s); + } + } + else if (c == '*') + { + if (_state != ParseState::elementName) + throw XmlException("ill formed XPath"s); + _location = Location::locationChildrenNoName; + } + else if (c == '/') + { + if (_state != ParseState::elementName) + throw XmlException("ill formed XPath"s); + if (pos == 0) + { + _location = Location::locationRoot; + } + else if (_location == Location::locationChildren) + { + _location = Location::locationAllChildren; + } + else + { + throw XmlException("ill formed XPath"s); + } + } else { switch (_state) @@ -132,7 +177,9 @@ namespace tinyxml2 } ElementProperties() {} // an empty property set - const std::string& Name() const { return _name; } + const std::string& Name() const { return _name; } + const Location& LocationType() const { return _location; } + bool Match (const XMLElement * element) const { // n.b. we only match attributes here, not the element name (type) @@ -148,11 +195,123 @@ namespace tinyxml2 for (auto const & attr : _attributes) element -> SetAttribute (attr .Name() .c_str(), attr .Value() .c_str()); } + + XMLElement * Locate(XMLElement *element, XMLElement *lastLocationElement = nullptr)const + { + auto ret = Locate(const_cast(element), const_cast(lastLocationElement)); + return const_cast(ret); + } + + //locate the element + //element = curLocation + //frontElement=lastLocation(not must parent, example A//B) + const XMLElement * Locate(const XMLElement *element,const XMLElement *frontElement)const + { + if (frontElement == nullptr) return nullptr; + + const XMLElement * retElement = nullptr;//return element + switch (_location) + { + case Location::locationChildren: + if(element == nullptr) + retElement= frontElement->FirstChildElement(_name.c_str()); + else + retElement = element->NextSiblingElement(_name.c_str()); + break; + case Location::locationMyself: + retElement = frontElement; + break; + case Location::locationParent: + retElement = frontElement->Parent()->ToElement(); + break; + case Location::locationChildrenNoName: + if (!element) + { + retElement = frontElement->FirstChildElement(); + } + else + { + retElement = element->NextSiblingElement(); + } + break; + case Location::locationAllChildren: + { + assert(frontElement); + std::vector searchEleList; + if (element == nullptr) + { + searchEleList = { frontElement ,frontElement->FirstChildElement() }; + } + else + { + //get element->xx->xx->lastLocationElement list + searchEleList = { element }; + auto lastpath = element; + while (auto parentEle = lastpath->Parent()->ToElement()) + { + searchEleList.push_back(parentEle); + + if (searchEleList.back() == frontElement) + break; + + lastpath = parentEle; + } + assert(searchEleList.back() != nullptr); + if (searchEleList.back() != frontElement) + break; + //reverse to lastLocationElement->xx->xx->element list + std::reverse(searchEleList.begin(), searchEleList.end()); + } + + //search lastLocationElement----->element,all children will be checked + const char* eleName = _name.c_str(); + while (searchEleList.size()) + { + while (searchEleList.back() == nullptr && searchEleList.size()) + { + searchEleList.pop_back(); + if (searchEleList.empty()) break; + searchEleList.back() = searchEleList.back()->NextSiblingElement(); + } + if (searchEleList.empty()) break; + + auto& lastEle = searchEleList.back(); + if (lastEle != frontElement && lastEle != element + && _name == lastEle->Value()) + { + retElement = lastEle; + break; + } + else if (auto ele = lastEle->FirstChildElement()) + { + searchEleList.push_back(ele); + continue; + } + + //search the children where is a target element + lastEle = lastEle->NextSiblingElement(); + } + }break; + case Location::locationFunction: + //TODO some function like A[0],A[price<3],A[size()-1] + break; + case Location::locationRoot: + retElement = frontElement->GetDocument()->RootElement(); + break; + default: + break; + } + return retElement; + } + private: std::string _name; - attribute_list_t _attributes; - enum class ParseState { elementName, attributeFilter, attributeName, attributeAssignment, attributeValue } _state {ParseState::elementName}; - }; // ElementProperties + attribute_list_t _attributes; + enum class ParseState { elementName, attributeFilter, attributeName, attributeAssignment, attributeValue }; + + ParseState _state {ParseState::elementName}; + Location _location = { Location::locationChildren };//use to relocate the level in element + }; // ElementProperties template using element_path_location_t = std::pair; @@ -162,50 +321,71 @@ namespace tinyxml2 template inline element_path_t element_path_from_xpath (XE * root, const std::string & xpath) { - if (!root) + //if there is //B, list is /B + //if there is A//B, list is A->/B + //if there is A//B/C, list is A->/B->C + //if there is A//B[@p='v']/C, list is A->/B[@p='v']->C + //if there is /A//B/C, list is A->/B->C + + if (!root || xpath.empty()) throw XmlException ("null element"s); element_path_t ep; // split the path size_t start = 0; - size_t pos; + size_t pos = 0; // set element at head of selection branch // if path starts with '/' then it is relative to document, otherwise relative to element passed in // first element in selection branch is the root and only children of the root are considered // for document-based paths, this works because there can only be one document element - if (!xpath.empty() && xpath[0] == '/') + if (xpath.size() >= 2 && xpath[0] == '/' && xpath[1] != '/') { // document is not an element so needs special handling // advance to the actual document element // note that document element must still appear in path, so we have to step over it - ++start; - if ((pos = xpath .find ('/', start)) != std::string::npos) + start++; + pos = xpath.find('/', start); + + auto filter = ElementProperties(xpath.substr(start, pos - start), 0); + auto element = root->GetDocument()->RootElement(); + if (element && !filter.Name().empty()) { - auto filter = ElementProperties (xpath .substr (start, pos - start)); - auto element = root -> GetDocument() -> RootElement(); - if (element && !filter .Name() .empty()) - { - if (filter .Name() != std::string (element -> Name())) - throw XmlException ("document element name mismatch"s); - } - ep .emplace_back (std::make_pair (filter, element)); - start = pos + 1; + if (filter.Name() != std::string(element->Name())) + throw XmlException("document element name mismatch"s); } + ep.emplace_back(std::make_pair(filter, element)); + start = pos; } else - ep .emplace_back (std::make_pair (ElementProperties (root -> Name()), root)); + { + ep.emplace_back(std::make_pair(ElementProperties(root->Name(), 0), root)); + } // continue with other elements along path - while ((pos = xpath .find ('/', start)) != std::string::npos) + while (start != std::string::npos) + { + //if (xpath.size()-1 > start && xpath[start]=='/' && xpath[start + 1] == '/') + //{ + // //if there is //B, substring is /B + // start++; + //} + if (xpath[start]=='/') + start++; + pos = xpath.find('/', start + 1); + + ep.emplace_back(std::make_pair(ElementProperties(xpath.substr(start, pos - start), start), nullptr)); + + start = pos; + } + + // and the final element,if "//B" is last string, step over it + if (start < xpath.size()) { - ep .emplace_back (std::make_pair (ElementProperties (xpath .substr (start, pos - start)), nullptr)); - start = pos + 1; + ep.emplace_back(std::make_pair(ElementProperties(xpath.substr(start, pos - start), start), nullptr)); } - // and the final element - ep .emplace_back (std::make_pair (ElementProperties (xpath .substr (start, pos - start)), nullptr)); - return ep; + return std::move(ep); } @@ -243,7 +423,9 @@ namespace tinyxml2 // descend and initialise first matching branch (if any) descend (_selectionPath .begin()); // remove the origin from the list so that iteration is only over child elements - _selectionPath .pop_front(); + + /*if(_selectionPath.begin()->first.LocationType() != ElementProperties::Location::locationRoot) + _selectionPath .pop_front();*/ } XE * operator *() const { return !_selectionPath .empty() ? _selectionPath .back() .second : nullptr; } bool operator == (const ElementIterator & iter) const { return iter.operator*() == this->operator*(); } @@ -262,82 +444,67 @@ namespace tinyxml2 } private: - bool descend (element_path_iterator_t ixSel) + bool descend(element_path_iterator_t parentixSel) { // recursively descend selection branch of matching elements - if (!ixSel -> second) + if (!parentixSel->second) return false; - auto parentElement = ixSel -> second; - - bool isBegin = ixSel == _selectionPath.begin(); + auto parentElement = parentixSel->second; + if (parentElement == nullptr) return false; - if (++ixSel == _selectionPath .end()) + auto ixSel = parentixSel; + if (++ixSel == _selectionPath.end()) return true; // we've found the first matching element - ixSel->second = parentElement->FirstChildElement(ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str()); - - //判断是否双斜杠,若不是根目录,则寻找下一级 - if (isBegin && ixSel->second ==nullptr) - { - std::vector searchEleList = { parentElement }; - const char* eleName = ixSel->first.Name().empty() ? nullptr : ixSel->first.Name().c_str(); - - auto lastEle = searchEleList.back(); - while (searchEleList.size()) - { - auto& lastEle = searchEleList.back(); - if (auto ele = lastEle->FirstChildElement(eleName)) - { - ixSel->second = ele; - break; - } - else if(auto ele = lastEle->FirstChildElement()) - { - searchEleList.push_back(ele); - continue; - } + const ElementProperties& eleppt = ixSel->first; - //至少要next一次 - if (!(lastEle = lastEle->NextSiblingElement())) - { - while (searchEleList.back() == nullptr && searchEleList.size()) - { - searchEleList.pop_back(); - if (searchEleList.empty()) break; - searchEleList.back() = searchEleList.back()->NextSiblingElement(); - } - } - } - } - while (ixSel -> second) + while (ixSel->second = eleppt.Locate(ixSel->second, parentElement)) { - if (ixSel -> first .Match (ixSel -> second)) + if (ixSel->first.Match(ixSel->second)) { - if (descend (ixSel)) + if (descend(ixSel)) return true; } - // move sideways - ixSel -> second = ixSel -> second -> NextSiblingElement (ixSel -> first .Name() .empty() ? nullptr : ixSel -> first .Name() .c_str()); } - return false; // no matching elements at this depth + return false; } - void traverse (element_path_iterator_t ixSel) + void traverse(element_path_iterator_t ixSel) { // to find next element we can go sideways or up and then down // traverse() does the moves across the xml tree, descend() then explores each potential new branch // note that this method can only be called once the selection has been initialised - while ((ixSel -> second = ixSel -> second -> NextSiblingElement (ixSel -> first .Name() .empty() ? nullptr : ixSel -> first .Name() .c_str()))) + const ElementProperties& eleppt = ixSel->first; + XE* parentElement = nullptr; + if (ixSel != _selectionPath.begin()) { - if (ixSel -> first .Match (ixSel -> second)) + auto parentixSel = ixSel; + --parentixSel; + parentElement = parentixSel->second; + } + + if (parentElement) + { + while (ixSel->second = eleppt.Locate(ixSel->second, parentElement)) { - if (descend (ixSel)) - return; + if (ixSel->first.Match(ixSel->second)) + { + if (descend(ixSel)) + { + return; + } + } } } + + //clear cur element + ixSel->second = nullptr; + // no siblings or sibling branches match, go up a level (unless already at origin) - if (ixSel != _selectionPath .begin()) - traverse (--ixSel); + if (ixSel != _selectionPath.begin()) + { + traverse(--ixSel); + } } private: @@ -603,15 +770,6 @@ namespace tinyxml2 return append_element (parent, xpath, attributes, text, false); } - //touch family - inline XMLElement * touch_element(XMLElement * parent, const std::string & xpath, const attribute_list_t & attributes = {}, const std::string & text = "") - { - auto ele = find_element(parent, xpath); - if(ele==nullptr) ele = append_element(parent, xpath, attributes, text, true); - assert(ele); - return ele; - } - inline XMLElement * insert_next_element (XMLElement * sibling, const std::string & name, const attribute_list_t & attributes = {}, const std::string & text = ""s) { From 3d5a87871214a46f184c10463d5becf456199430 Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Fri, 16 Nov 2018 17:43:44 +0800 Subject: [PATCH 7/9] fix a test program B/ => B/* to get over all children of element --- test/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/main.cpp b/test/main.cpp index b783e57..769d9d3 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -296,7 +296,7 @@ int main() // iterate over all children, any name (type), of elements which are children of the document element cout << "iterate over all children, any name (type), of elements which are children of the document element" << endl; auto eA = doc -> FirstChildElement(); - for (auto cd : tinyxml2::selection (eA, "B/")) + for (auto cd : tinyxml2::selection (eA, "B/*")) cout << cd -> Name() << " = " << text (cd) << " id=" << attribute_value (cd, "id") << endl; cout << "=================================================" << endl << endl; } From 4e9fcb93b46cb9aa744ea00c0ccae2c69ec9c365 Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Fri, 16 Nov 2018 18:30:14 +0800 Subject: [PATCH 8/9] fix some parent and children change problem add parent/children change test --- test/main.cpp | 22 ++++++++++++++-------- tixml2ex.h | 7 +++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/test/main.cpp b/test/main.cpp index 769d9d3..9876119 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -108,6 +108,7 @@ int main() ,{R"(//A//B//C)",3} ,{ R"(A/B[@code='1'])",2 } ,{ R"(A/B[@code='1']/C)",3 } + ,{ R"(/A/../B[@code='1']/C)",4 } }; for (auto& it : xpathElementCount) @@ -150,31 +151,36 @@ int main() } assert(count== 5); if (count != 5) - { cout << "A//C expect 5 but actrul equal to "<RootElement(),"*/B/C"s)) - { ++count; - } assert(count == 0); count = 0; for (auto it : tinyxml2::selection(doc->RootElement(), "*/C"s)) - { ++count; - } assert(count == 5); count = 0; for (auto it : tinyxml2::selection(doc->RootElement(), "/A/*/C"s)) - { ++count; - } assert(count == 5); + count = 0; + for (auto it : tinyxml2::selection(doc->RootElement(), "/A/B[@id='one']/../B[@id='three']/C"s)) + ++count; + assert(count == 2); + + count = 0; + for (auto it : tinyxml2::selection(doc->RootElement(), "/A/B/.[@id='one']/C"s)) + ++count; + assert(count == 3); + + for (auto eC : tinyxml2::selection(doc->RootElement(), "//B[@id='one']"s)) + cout << eC->Name() << " id = " << attribute_value(eC,"id") << endl; + cout << "=================================================" << endl << endl; } catch (tinyxml2::XmlException & e) diff --git a/tixml2ex.h b/tixml2ex.h index ee79b19..393de76 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -1,4 +1,4 @@ -/* +/* tinyxml2ex - a set of add-on classes and helper functions bringing C++11/14 features, such as iterators, strings and exceptions, to tinyxml2 @@ -222,7 +222,7 @@ namespace tinyxml2 retElement = frontElement; break; case Location::locationParent: - retElement = frontElement->Parent()->ToElement(); + retElement = frontElement->Parent()->ToElement(); break; case Location::locationChildrenNoName: if (!element) @@ -301,6 +301,9 @@ namespace tinyxml2 default: break; } + if (retElement == element)//if it's myself return false + retElement = nullptr; + return retElement; } From ad80fb6b24717db12d6e52e2c7b6e3eb61e1878b Mon Sep 17 00:00:00 2001 From: ShunmingGuo <631550367@qq.com> Date: Sat, 17 Nov 2018 10:20:17 +0800 Subject: [PATCH 9/9] add touch family --- tixml2ex.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tixml2ex.h b/tixml2ex.h index 393de76..0c90abf 100644 --- a/tixml2ex.h +++ b/tixml2ex.h @@ -1,4 +1,4 @@ -/* +/* tinyxml2ex - a set of add-on classes and helper functions bringing C++11/14 features, such as iterators, strings and exceptions, to tinyxml2 @@ -773,6 +773,14 @@ namespace tinyxml2 return append_element (parent, xpath, attributes, text, false); } + //touch family + inline XMLElement * touch_element(XMLElement * parent, const std::string & xpath, const attribute_list_t & attributes = {}, const std::string & text = "") + { + auto ele = find_element(parent, xpath); + if (ele == nullptr) ele = append_element(parent, xpath, attributes, text, true); + assert(ele); + return ele; + } inline XMLElement * insert_next_element (XMLElement * sibling, const std::string & name, const attribute_list_t & attributes = {}, const std::string & text = ""s) {