Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
151 lines (83 sloc) 117 KB

 

 

 

 

 

(C++) Xml

 

'Extensible Markup Language (XML) is a set of rules for encoding documents in machine-readable form' [1].

 

 

 

 

 

XML code snippets

 

 

 

 

 

 

C++ XML parsers

 

Incomplete list.

 

 

 

 

 

 

References

 

  1. Wikipedia page about XML

Technical facts

 

 

 

 

 

 

./CppXml/CppXml.pri

 


INCLUDEPATH += \     ../../Classes/CppXml SOURCES += \     ../../Classes/CppXml/xml.cpp HEADERS  += \     ../../Classes/CppXml/xml.h OTHER_FILES += \     ../../Classes/CppXml/Licence.txt

 

 

 

 

 

./CppXml/xml.h

 


//--------------------------------------------------------------------------- /* XML functions Copyright 2014-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppXml.htm //--------------------------------------------------------------------------- #ifndef RIBI_XML_H #define RIBI_XML_H #include <map> #include <set> #include <string> #include <vector> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include <boost/lexical_cast.hpp> #pragma GCC diagnostic pop namespace ribi { namespace xml { std::string GetVersion() noexcept; std::vector<std::string> GetVersionHistory() noexcept; ///Convert a std::string to single-line XML ///For example, a std::string with tag name "cat_name" and content "Kitty" becomes /// <cat_name>Kitty</cat_name> ///The data can be converted back with XmlToStr template <class T, class U> std::string ToXml(   const T& tag_name,   const U& content) {   std::stringstream s;   s << "<"  << tag_name << ">" << content << "</" << tag_name << ">";   //No test here, as ToXml is used in testing FromXml   return s.str(); } ///Convert a std::string to single-line XML ///For example, a std::string with tag name "cat_name" and content "Kitty" becomes /// <cat_name>Kitty</cat_name> ///The data can be converted back with XmlToStr template <class T, class U> std::string ToXml(   const T& tag_name,   const U& content,   const std::function<std::string(const T&)> tag_to_str_function,   const std::function<std::string(const U&)> content_to_str_function   ) {   std::stringstream s;   s     << "<"  << tag_to_str_function(tag_name) << ">"     << content_to_str_function(content)     << "</" << tag_to_str_function(tag_name) << ">";   //No test here, as ToXml is used in testing FromXml   return s.str(); } ///Convert a container to single-line XML ///For example, a std::vector<std::string> with elements {"cat","dog"} and name "animals" becomes /// <animals><0>cat</0><1>dog</1></animals> ///The data can be converted back with FromXml template <class Iter> std::string ToXml(   const std::string& tag_name,   Iter begin,   const Iter& end) {   std::stringstream s;   int i = 0;   for ( ; begin!=end; ++begin)   {     const std::string index_tag_name = boost::lexical_cast<std::string>(i);     const std::string index_content  = boost::lexical_cast<std::string>(*begin);     s << ToXml(index_tag_name,index_content);     ++i;   }   const std::string content = s.str();   //No test here, as this function is used in XmlToVector   return ToXml(tag_name,content); } // // // Above: order-dependent for compiling // Below: alphabetic ordering // // ///Convert a single-line XML to its content and its tag name ///For example, the XML line "<cat_name>Kitty</cat_name>" ///becomes a std::pair with elements {"cat_name","Kitty"} ///The data can be converted back with ToXml template <class T = std::string, class U = std::string> std::pair<T,U> FromXml(const std::string& xml) {   assert(!xml.empty());   assert(xml[0] == '<');   assert(xml[xml.size() - 1] == '>');   assert(xml.find('>') != std::string::npos);   const int tag_name_sz = static_cast<int>(xml.find('>')) - 1;   const std::string tag_name = xml.substr(1,tag_name_sz);   assert(xml.find_last_of('/') != std::string::npos);   const int content_sz = static_cast<int>(xml.find_last_of('/')) - tag_name_sz - 3;   const std::string content = xml.substr(tag_name.size() + 2,content_sz);   const std::pair<T,U> p {     boost::lexical_cast<T>(tag_name),     boost::lexical_cast<U>(content)   };   assert(ToXml(p.first,p.second) == xml);   return p; } ///Convert a single-line XML to its content and its tag name ///For example, the XML line "<cat_name>Kitty</cat_name>" ///becomes a std::pair with elements {"cat_name","Kitty"} ///The data can be converted back with ToXml template <class T, class U> std::pair<T,U> FromXml(   const std::string& xml,   const std::function<T(const std::string&)> str_to_tag_function,   const std::function<U(const std::string&)> str_to_content_function   ) {   assert(!xml.empty());   assert(xml[0] == '<');   assert(xml[xml.size() - 1] == '>');   assert(xml.find('>') != std::string::npos);   const int tag_name_sz = static_cast<int>(xml.find('>')) - 1;   const std::string tag_name = xml.substr(1,tag_name_sz);   assert(xml.find_last_of('/') != std::string::npos);   const int content_sz = static_cast<int>(xml.find_last_of('/')) - tag_name_sz - 3;   const std::string content = xml.substr(tag_name.size() + 2,content_sz);   const std::pair<T,U> p {     str_to_tag_function(tag_name),     str_to_content_function(content)   };   //Cannot do this debug check anymore, as one would need   //a tag_to_str_function and content_to_str_function:   //assert(ToXml(p.first,p.second,tag_to_str_function,content_to_str_function) == xml);   return p; } ///Convert a map to single-line XML ///For example, an int-to-string map of /// {{1,"one"}, {2,"two"}, {4,"four"}} and tag name "numbers" becomes the following XML string: /// /// <numbers> ///   <1>one</1> ///   <2>two</2> ///   <4>four</4> /// </numbers> /// /// <numbers> ///   <0><key>1</key><value>one</value></0> ///   <1><key>2</key><value>two</value></1> ///   <2><key>4</key><value>four</value></2> /// </numbers> /// ///(indentation is added for readability) ///The data can be converted back with XmlToPtrs template <   class TagType = std::string,   class KeyType = std::string,   class ValueType = std::string> std::string MapToXml(   const TagType& tag_name,   const std::map<KeyType,ValueType> m   ) {   std::stringstream s;   const auto end = std::end(m);   for (/* const */ auto begin = std::begin(m); begin!=end; ++begin)   {     s << ToXml( (*begin).first, (*begin).second);   }   const std::string content { s.str() };   return ToXml(tag_name,content); } ///Convert a map to single-line XML ///For example, an int-to-string map of /// {{1,"one"}, {2,"two"}, {4,"four"}} and tag name "numbers" becomes the following XML string: /// /// <numbers> ///   <1>one</1> ///   <2>two</2> ///   <4>four</4> /// </numbers> /// /// <numbers> ///   <0><key>1</key><value>one</value></0> ///   <1><key>2</key><value>two</value></1> ///   <2><key>4</key><value>four</value></2> /// </numbers> /// ///(indentation is added for readability) ///The data can be converted back with XmlToPtrs template <class TagType, class KeyType, class ValueType> std::string MapToXml(   const TagType& tag_name,   const std::map<KeyType,ValueType> m,   const std::function<std::string(const TagType&  )> tag_to_str_function,   const std::function<std::string(const KeyType&  )> key_to_str_function,   const std::function<std::string(const ValueType&)> value_to_str_function   ) {   std::stringstream s;   const auto end = std::end(m);   for (/* const */ auto begin = std::begin(m); begin!=end; ++begin)   {     s << ToXml( (*begin).first, (*begin).second, key_to_str_function, value_to_str_function);   }   const std::string content { s.str() };   return ToXml(tag_to_str_function(tag_name),content); } ///Convert a container of pointers to single-line XML ///For example, a std::vector<boost::shared_ptr<std::string>> ///dynamically allocated std::strings {"cat","dog"} and tag name "animals" becomes /// <animals><0>cat</0><1>dog</1></animals> ///The data can be converted back with XmlToPtrs template <class Iter> std::string PtrsToXml(   const std::string& tag_name,   Iter begin,   const Iter& end ) {   std::stringstream s;   int i = 0;   for ( ; begin!=end; ++begin)   {     const std::string index_tag_name = boost::lexical_cast<std::string>(i);     const std::string index_content  = boost::lexical_cast<std::string>( *(*begin) );     s << ToXml(index_tag_name,index_content); //StrToXml     ++i;   }   const std::string content = s.str();   //No test here, as this function is used in XmlToPtrs   return ToXml(tag_name,content); //StrToXml } template <class T> const std::string SetToXml(   const std::string& tag_name,   const std::set<T>& content) {   return ToXml(tag_name,content.begin(),content.end()); } ///Split an XML std::string into its parts //From http://www.richelbilderbeek.nl/CppSplitXml.htm std::vector<std::string> SplitXml(const std::string& s); ///Strip the XML tags of an XML item ///For example '<tag>text</tag>' becomes 'text' ///Note that also '<any_tag>text</other_tag>' fails //From http://www.richelbilderbeek.nl/CppStripXmlTag.htm std::string StripXmlTag(const std::string& s); ///Convert a std::string to single-line XML ///For example, a std::string with tag name "cat_name" and content "Kitty" becomes /// <cat_name>Kitty</cat_name> ///The data can be converted back with XmlToStr //const std::string StrToXml( //  const std::string& tag_name, //  const std::string& content) //{ //  return ToXml(tag_name,content); //} #ifndef NDEBUG void Test() noexcept; #endif ///Convert a std::vector to single-line XML ///For example, a std::vector with elements {"cat","dog"} and name "animals" becomes /// <animals><0>cat</0><1>dog</1></animals> ///The data can be converted back with XmlToVector template <class T> std::string VectorToXml(   const std::string& tag_name,   const std::vector<T>& v ) {   //No test here, as this function is used in XmlToVector   return ToXml(tag_name,v.begin(),v.end()); } ///Convert a single-line XML to a map ///The data can be converted back with MapToXml template <class KeyType, class ValueType> std::pair<std::string,std::map<KeyType,ValueType>> XmlToMap(     const std::string& s,     const std::function<KeyType(const std::string&)> str_to_key_function,     const std::function<ValueType(const std::string&)> str_to_value_function   ) {   assert(!s.empty());   assert(s[           0] == '<');   assert(s[s.size() - 1] == '>');   assert(s.find('>') != std::string::npos);   //Read the name tag   //<name>...</name>   const int tag_name_sz = static_cast<int>(s.find('>')) - 1;   const std::string tag_name = s.substr(1,tag_name_sz);   std::map<KeyType,ValueType> map;   //Remove the name tags   std::string t = s.substr(tag_name_sz + 2,s.size() - (2 * tag_name_sz) - 5);   for (int i=0; !t.empty(); ++i)   {     //Read the index tags and item     //<index>item</index>     assert(!t.empty());     assert(t[0] == '<');     assert(t[t.size() - 1] == '>');     assert(t.find('>') != std::string::npos);     const int index_sz = static_cast<int>(t.find('>')) - 1;     const std::string index = t.substr(1,index_sz);     //assert(i == boost::lexical_cast<int>(index));     assert(t.find('/') != std::string::npos);     const int item_sz = static_cast<int>(t.find('/')) - index_sz - 3;     const std::string item_str = t.substr(index.size() + 2,item_sz);     const int total_sz = (2 * index_sz) + item_sz + 5;     t = t.substr(total_sz,t.size() - total_sz);     map.insert(       std::make_pair(         str_to_key_function(index),         str_to_value_function(item_str)       )     );   }   //Cannot do the test below, as one would need a key_to_str_function and content_to_str_function   //assert(MapToXml(tag_name,map.begin(),map.end(),key_to_str_function,content_to_str_function) == s);   return std::make_pair(tag_name,map); } ///Pretty-print an XML std::string by indenting its elements //From http://www.richelbilderbeek.nl/CppXmlToPretty.htm std::vector<std::string> XmlToPretty(const std::string& s) noexcept; ///Convert a single-line XML to a std::vector of smart pointers and its name ///For example, the XML line "<animals><0>cat</0><1>dog</1></animals>" ///becomes a std::vector of smart pointers of dynamically allocated strings ///with values {"cat","dog"} and the tag name "animals" ///The conversion from std::string to smart pointer needs to be supplied, for example ///a conversion from string to a smart pointer of a dynamically allocated string: /// /// const std::function<const boost::shared_ptr<std::string>(const std::string&)> str_to_ptr_function { ///   [](const std::string& s) ///   { ///     return boost::make_shared<std::string>(s); ///   } /// }; /// ///The data can be converted back with PtrsToXml template <class T> std::pair<     std::string,     std::vector<boost::shared_ptr<T>>   >   XmlToPtrs(     const std::string& s,     const std::function<const boost::shared_ptr<T>(const std::string&)> str_to_ptr_function   ) {   assert(!s.empty());   assert(s[           0] == '<');   assert(s[s.size() - 1] == '>');   assert(s.find('>') != std::string::npos);   //Read the name tag   //<name>...</name>   const int name_sz = static_cast<int>(s.find('>')) - 1;   const std::string name = s.substr(1,name_sz);   std::vector<boost::shared_ptr<T>> v;   //Remove the name tags   std::string t = s.substr(name_sz + 2,s.size() - (2 * name_sz) - 5);   for (int i=0; !t.empty(); ++i)   {     //Read the index tags and item     //<index>item</index>     assert(!t.empty());     assert(t[0] == '<');     assert(t[t.size() - 1] == '>');     assert(t.find('>') != std::string::npos);     const int index_sz = static_cast<int>(t.find('>')) - 1;     const std::string index = t.substr(1,index_sz);     assert(i == boost::lexical_cast<int>(index));     assert(t.find('/') != std::string::npos);     const int item_sz = static_cast<int>(t.find('/')) - index_sz - 3;     const std::string item_str = t.substr(index.size() + 2,item_sz);     const int total_sz = (2 * index_sz) + item_sz + 5;     t = t.substr(total_sz,t.size() - total_sz);     const boost::shared_ptr<T> item { str_to_ptr_function(item_str) };     v.push_back(item);   }   assert(PtrsToXml(name,v.begin(),v.end()) == s);   return std::make_pair(name,v); } ///Convert a single-line XML to its content and its tag name ///For example, the XML line "<cat_name>Kitty</cat_name>" ///becomes a std::pair with elements {"cat_name","Kitty"} ///The data can be converted back with StrToXml //const std::pair<std::string,std::string> XmlToStr( //  const std::string& s); ///Convert a single-line XML to a std::vector and its name ///For example, the XML line "<animals><0>cat</0><1>dog</1></animals>" ///becomes a std::vector with elements {"cat","dog"} and the name "animals" ///The data can be converted back with VectorToXml std::pair<std::string,std::vector<std::string>> XmlToVector(const std::string& s); } //~namespace xml } //~namespace ribi #endif // RIBI_XML_H

 

 

 

 

 

./CppXml/xml.cpp

 


//--------------------------------------------------------------------------- /* XML functions Copyright 2014-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppXml.htm //--------------------------------------------------------------------------- #include "xml.h" #include <string> #include <sstream> #include <vector> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include <boost/lexical_cast.hpp> #include <boost/make_shared.hpp> #include <boost/shared_ptr.hpp> #include "trace.h" #pragma GCC diagnostic pop //From http://www.richelbilderbeek.nl/CppCanLexicalCast.htm template <class TargetType, class SourceType> bool CanLexicalCast(const SourceType& from) {   try   {     boost::lexical_cast<TargetType>(from);   }   catch (boost::bad_lexical_cast)   {     return false;   }   catch (...)   {     assert(!"Something unexpected happened");     throw;   }   return true; } std::string ribi::xml::GetVersion() noexcept {   return "1.1"; } std::vector<std::string> ribi::xml::GetVersionHistory() noexcept {   return {     "201x-xx-xx: Version 1.0: initial version",     "2014-02-27: Version 1.1: started versioning"   }; } std::vector<std::string> ribi::xml::SplitXml(const std::string& s) {   std::vector<std::string> v;   std::string::const_iterator i = s.begin();   std::string::const_iterator j = s.begin();   const std::string::const_iterator end = s.end();   while (j!=end)   {     ++j;     if ((*j=='>' || *j == '<') && std::distance(i,j) > 1)     {       std::string t;       std::copy(         *i=='<' ? i   : i+1,         *j=='>' ? j+1 : j,         std::back_inserter(t));       v.push_back(t);       i = j;     }   }   return v; } std::string ribi::xml::StripXmlTag(const std::string& s) {   if (s.empty()) return "";   if (s[0]!='<') return "";   if (s[s.size() - 1]!='>') return "";   const int begin = s.find_first_of('>');   if (begin == static_cast<int>(std::string::npos)) return "";   const int end = s.find_last_of('<');   if (end == static_cast<int>(std::string::npos)) return "";   if (begin > end) return "";   assert(begin < end);   const std::string tag_left = s.substr(0,begin+1);   assert(!tag_left.empty());   assert(tag_left[0] == '<');   assert(tag_left[tag_left.size() - 1] == '>');   const std::string tag_left_text = tag_left.substr(1,tag_left.size() - 2);   if (tag_left_text.empty()) return "";   const std::string tag_right = s.substr(end,s.size() - end);   if (tag_right.size() < 2) return "";   assert(!tag_right.empty());   assert(tag_right[0] == '<');   assert(tag_right[tag_right.size() - 1] == '>');   const std::string tag_right_text = tag_right.substr(2,tag_right.size() - 3);   if (tag_right_text.empty()) return "";   if (tag_left_text != tag_right_text) return "";   const std::string text = s.substr(begin + 1,end - begin - 1);   return text; } #ifndef NDEBUG void ribi::xml::Test() noexcept {   {     static bool is_tested{false};     if (is_tested) return;     is_tested = true;   }   //StripXmlTag   {     assert(StripXmlTag("<my_tag>text</my_tag>") == "text");     assert(StripXmlTag("<mytag>text</mytag>") == "text");     assert(StripXmlTag("<tags>text</tags>") == "text");     assert(StripXmlTag("<tag>text</tag>") == "text");     assert(StripXmlTag("<tg>text</tg>") == "text");     assert(StripXmlTag("<t>text</t>") == "text");     assert(StripXmlTag("<x>y</x>") == "y");     assert(StripXmlTag("<x>y</x></x>") == "y</x>");     assert(StripXmlTag("<x><x>y</x>") == "<x>y");     assert(StripXmlTag("<x><x>y</x></x>") == "<x>y</x>");     assert(StripXmlTag("<x>y</z>") == "");     assert(StripXmlTag("<x>y<x>") == "");     assert(StripXmlTag("<>y<>") == "");     assert(StripXmlTag("<>y</>") == "");     assert(StripXmlTag("<x>y") == "");     assert(StripXmlTag("<x></x>") == "");   }   //StrToXml and XmlToStr   {     const std::vector<std::string> v { "a", "ab", "abc", " ", "" };     const std::size_t sz = v.size();     for (std::size_t i=0; i!=sz; ++i)     {       const std::string tag_name = v[i];       for (std::size_t j=0; j!=sz; ++j)       {         const std::string content = v[j];         const std::string xml = ToXml(tag_name,content);         assert(FromXml(xml).first  == tag_name);         assert(FromXml(xml).second == content);       }     }   }   //MapToXml   {     //Use int to std::string map     {       //Create a map       typedef int KeyType;       typedef std::string ValueType;       std::map<KeyType,ValueType> m;       m.insert( std::make_pair(1,"one") );       m.insert( std::make_pair(2,"two") );       m.insert( std::make_pair(4,"four") );       const std::string tag_name = "integers";       //Convert map to XML       const std::string xml = MapToXml(tag_name,m);       //Convert XML back to map       const std::function<KeyType(const std::string&)>& str_to_key_function {         [](const std::string& s)         {           assert(CanLexicalCast<KeyType>(s));           return boost::lexical_cast<KeyType>(s);         }       };       const std::function<ValueType(const std::string&)>& str_to_value_function {         [](const std::string& s)         {           assert(CanLexicalCast<ValueType>(s));           return boost::lexical_cast<ValueType>(s);         }       };       const std::pair<std::string,std::map<KeyType,ValueType>> p {         XmlToMap<KeyType,ValueType>(xml,str_to_key_function,str_to_value_function)       };       assert(p.first == tag_name);       assert(p.second.size() == m.size());       assert(std::equal(m.begin(),m.end(),p.second.begin()));       //Again convert pointers to XML       std::stringstream s;       s << MapToXml(p.first,p.second);     }     //Use std::string to int map     {       //Create a map       typedef std::string KeyType;       typedef int ValueType;       std::map<KeyType,ValueType> m;       m.insert( std::make_pair("one",1) );       m.insert( std::make_pair("two",2) );       m.insert( std::make_pair("four",4) );       const std::string tag_name = "integers";       //Convert map to XML       const std::string xml = MapToXml(tag_name,m);       //Convert XML back to map       const std::function<KeyType(const std::string&)>& str_to_key_function {         [](const std::string& s)         {           assert(CanLexicalCast<KeyType>(s));           return boost::lexical_cast<KeyType>(s);         }       };       const std::function<ValueType(const std::string&)>& str_to_value_function {         [](const std::string& s)         {           assert(CanLexicalCast<ValueType>(s));           return boost::lexical_cast<ValueType>(s);         }       };       const std::pair<std::string,std::map<KeyType,ValueType>> p {         XmlToMap<KeyType,ValueType>(xml,str_to_key_function,str_to_value_function)       };       assert(p.first == tag_name);       assert(p.second.size() == m.size());       assert(std::equal(m.begin(),m.end(),p.second.begin()));       //Again convert pointers to XML       std::stringstream s;       s << MapToXml(p.first,p.second) << '\n';     }     //Use int to boost::shared_ptr<const std::string> map     {       //Create a map       typedef std::string TagType;       typedef int KeyType;       typedef boost::shared_ptr<const std::string> ValueType;       const TagType tag_name { "integers again" };       std::map<KeyType,ValueType> m;       m.insert(         std::make_pair(           1,           boost::make_shared<const std::string>("one" )         )       );       m.insert( std::make_pair(4,boost::make_shared<const std::string>("four")) );       m.insert( std::make_pair(9,boost::make_shared<const std::string>("nine")) );       //Convert map to XML       const std::function<std::string(const TagType&)> tag_to_str_function {         [](const TagType& tag)         {           return tag;         }       };       const std::function<std::string(const KeyType&  )> key_to_str_function {         [](const KeyType& key)         {           assert(CanLexicalCast<std::string>(key));           return boost::lexical_cast<std::string>(key);         }       };       const std::function<std::string(const ValueType&)> value_to_str_function {         [](const ValueType& value)         {           return *value;         }       };       const std::string xml {         MapToXml(tag_name,m,tag_to_str_function,key_to_str_function,value_to_str_function)       };       //Convert XML back to map       //const std::function<TagType(const std::string&)>& str_to_tag_function {       //  [](const std::string& s)       //  {       //    return s;       //  }       //};       const std::function<KeyType(const std::string&)>& str_to_key_function {         [](const std::string& s)         {           assert(CanLexicalCast<KeyType>(s));           return boost::lexical_cast<KeyType>(s);         }       };       const std::function<ValueType(const std::string&)>& str_to_value_function {         [](const std::string& s)         {           return boost::make_shared<const std::string>(s);         }       };       const std::pair<std::string,std::map<KeyType,ValueType>> p {         XmlToMap<KeyType,ValueType>(xml,str_to_key_function,str_to_value_function)       };       assert(p.first == tag_name);       assert(p.second.size() == m.size());       assert(         std::equal(m.begin(),m.end(),p.second.begin(),           [key_to_str_function,value_to_str_function](             const std::pair<KeyType,ValueType>& lhs, const std::pair<KeyType,ValueType>& rhs)           {             return key_to_str_function(lhs.first) == key_to_str_function(rhs.first)               && value_to_str_function(lhs.second) == value_to_str_function(rhs.second);           }         )       );       //Again convert pointers to XML       std::stringstream s;       s << MapToXml(tag_name,m,tag_to_str_function,key_to_str_function,value_to_str_function);     }   }   //SetToXml and XmlToSet   {     const std::set<std::string> content { "cats", "dog", "zebrafinch" };     const std::string tag_name = "animals";     const std::string xml = ToXml(tag_name,content.begin(),content.begin());     //const std::pair<std::string,std::set<std::string>> p { XmlToSet(xml) };     //assert(p.first == tag_name);     //assert(p.second == content);   }   //ToXml and FromXml   {     //tag: std::string, content: std::string     {       typedef std::string TagType;       typedef std::string ContentType;       const TagType     tag_name { "name"  };       const ContentType content  { "Kitty" };       const std::string xml { ToXml(tag_name,content) };       const std::pair<TagType,ContentType> p { FromXml<TagType,ContentType>(xml) };       assert(p.first  == tag_name);       assert(p.second == content);     }     //tag: int, content: std::string     {       typedef int TagType;       typedef std::string ContentType;       const TagType     tag_name { 42  };       const ContentType content  { "The answer" };       const std::string xml { ToXml(tag_name,content) };       const std::pair<TagType,ContentType> p { FromXml<TagType,ContentType>(xml) };       assert(p.first  == tag_name);       assert(p.second == content);     }     //tag: int, content: std::string     {       typedef int ContentType;       typedef std::string TagType;       const TagType     tag_name { "The answer" };       const ContentType content  { 42 };       const std::string xml { ToXml(tag_name,content) };       const std::pair<TagType,ContentType> p { FromXml<TagType,ContentType>(xml) };       assert(p.first  == tag_name);       assert(p.second == content);     }     //tag: int, content: int     {       typedef std::string TagType;       typedef int ContentType;       const TagType     tag_name { 123 };       const ContentType content  { 456 };       const std::string xml { ToXml(tag_name,content) };       const std::pair<TagType,ContentType> p { FromXml<TagType,ContentType>(xml) };       assert(p.first  == tag_name);       assert(p.second == content);     }     //tag: std::string, content: boost::shared_ptr<const std::string>     {       typedef std::string TagType;       typedef boost::shared_ptr<const std::string> ContentType;       const TagType     tag_name { "name" };       const ContentType content  { boost::make_shared<const std::string>("Kitty") };       //Convert tag and content to XML       const std::function<std::string(const TagType&)> tag_to_str_function {         [](const TagType& t) { return t; }       };       const std::function<std::string(const ContentType&)> content_to_str_function {         [](const ContentType& c) { return *c; }       };       const std::string xml {         ToXml(tag_name,content,tag_to_str_function,content_to_str_function)       };       //Convert XML back to its tag and content       //with custom functions       const std::function<TagType(const std::string&)> str_to_tag_function {         [](const std::string& s) { return s; }       };       const std::function<ContentType(const std::string&)> str_to_content_function {         [](const std::string& s) { return boost::make_shared<const std::string>(s); }       };       //Check both conversion functions       //Cannot simply compare to tag_name and content, as these may be of any type       assert(tag_to_str_function(str_to_tag_function(tag_to_str_function(tag_name)))         ==   tag_to_str_function(                                        tag_name));       assert(content_to_str_function(str_to_content_function(content_to_str_function(content)))         ==   content_to_str_function(                                                content));       const std::pair<TagType,ContentType> p {         FromXml<TagType,ContentType>(           xml,           str_to_tag_function,           str_to_content_function         )       };       //Cannot simply compare to tag_name and content, as these may be of any type       assert(tag_to_str_function(    p.first ) == tag_to_str_function(    tag_name));       assert(content_to_str_function(p.second) == content_to_str_function(content ));     }     //tag: int, content: boost::shared_ptr<const std::string>     {       typedef int TagType;       typedef boost::shared_ptr<const std::string> ContentType;       const TagType     tag_name { 123 };       const ContentType content  { boost::make_shared<const std::string>("one-two-three") };       //Convert tag and content to XML       const std::function<std::string(const TagType&)> tag_to_str_function {         [](const TagType& t)         {           assert(CanLexicalCast<std::string>(t));           return boost::lexical_cast<std::string>(t);         }       };       const std::function<std::string(const ContentType&)> content_to_str_function {         [](const ContentType& c) { return *c; }       };       const std::string xml {         ToXml(tag_name,content,tag_to_str_function,content_to_str_function)       };       //Convert XML back to its tag and content       //with custom functions       const std::function<TagType(const std::string&)> str_to_tag_function {         [](const std::string& s)         {           assert(CanLexicalCast<TagType>(s));           return boost::lexical_cast<TagType>(s);         }       };       const std::function<ContentType(const std::string&)> str_to_content_function {         [](const std::string& s) { return boost::make_shared<const std::string>(s); }       };       //Check both conversion functions       //Cannot simply compare to tag_name and content, as these may be of any type       assert(tag_to_str_function(str_to_tag_function(tag_to_str_function(tag_name)))         ==   tag_to_str_function(                                        tag_name));       assert(content_to_str_function(str_to_content_function(content_to_str_function(content)))         ==   content_to_str_function(                                                content));       const std::pair<TagType,ContentType> p {         FromXml<TagType,ContentType>(           xml,           str_to_tag_function,           str_to_content_function         )       };       //Cannot simply compare to tag_name and content, as these may be of any type       assert(tag_to_str_function(    p.first ) == tag_to_str_function(    tag_name));       assert(content_to_str_function(p.second) == content_to_str_function(content ));     }   }   //VectorToXml and XmlToVector   {     const std::vector<std::string> content { "cats", "dog", "zebrafinch" };     const std::string tag_name = "animals";     const std::string xml = VectorToXml(tag_name,content);     assert(xml == "<animals><0>cats</0><1>dog</1><2>zebrafinch</2></animals>");     assert(xml == ToXml(tag_name,content.begin(),content.end()));     const std::pair<std::string,std::vector<std::string>> p { XmlToVector(xml) };     assert(p.first == tag_name);     assert(p.second == content);   }   //XmlToPretty   {     {       const std::vector<std::string> result {         XmlToPretty("<a>test</a>")       };       const std::vector<std::string> expected {         "<a>",         "test",         "</a>"       };       //std::copy(result.begin(),result.end(),std::ostream_iterator<std::string>(std::cerr,"\n"));       assert(result == expected);     }     {       const std::vector<std::string> result {         XmlToPretty("<a><b>test</b></a>")       };       const std::vector<std::string> expected {         "<a>",         "  <b>",         "  test",         "  </b>",         "</a>"       };       //std::copy(result.begin(),result.end(),std::ostream_iterator<std::string>(std::cerr,"\n"));       assert(result == expected);     }     {       const std::vector<std::string> result {         XmlToPretty("<a><b>this is</b><c>a test</c></a>")       };       const std::vector<std::string> expected {         "<a>",         "  <b>",         "  this is",         "  </b>",         "  <c>",         "  a test",         "  </c>",         "</a>"       };       //std::copy(result.begin(),result.end(),std::ostream_iterator<std::string>(std::cerr,"\n"));       assert(result == expected);     }   {     const std::string s = "<a>A</a>";     const std::vector<std::string> split = SplitXml(s);     const std::vector<std::string> split_expected       =       {         "<a>",         "A",         "</a>"       };     assert(split == split_expected);     const std::vector<std::string> pretty = XmlToPretty(s);     const std::vector<std::string> pretty_expected       =       {         "<a>",         "A",         "</a>"       };     assert(pretty == pretty_expected);   }   {     const std::string s = "<a>A<b>B</b></a>";     const std::vector<std::string> split = SplitXml(s);     const std::vector<std::string> split_expected       =       {         "<a>",         "A",         "<b>",         "B",         "</b>",         "</a>"       };     assert(split == split_expected);     const std::vector<std::string> pretty = XmlToPretty(s);     const std::vector<std::string> pretty_expected       =       {         "<a>",         "A",         "  <b>",         "  B",         "  </b>",         "</a>"       };     assert(pretty == pretty_expected);   }   {     const std::string s = "<a>A<b>B1</b><b>B2</b></a>";     const std::vector<std::string> split = SplitXml(s);     const std::vector<std::string> split_expected       =       {         "<a>",         "A",         "<b>",         "B1",         "</b>",         "<b>",         "B2",         "</b>",         "</a>"       };     assert(split == split_expected);     const std::vector<std::string> pretty = XmlToPretty(s);     const std::vector<std::string> pretty_expected       =       {         "<a>",         "A",         "  <b>",         "  B1",         "  </b>",         "  <b>",         "  B2",         "  </b>",         "</a>"       };     assert(pretty == pretty_expected);   }   } } #endif std::vector<std::string> ribi::xml::XmlToPretty(const std::string& s) noexcept {   std::vector<std::string> v = SplitXml(s);   int n = -2;   for (std::string& s: v)   {     assert(!s.empty());     if (s[0] == '<' && s[1] != '/')     {       n+=2;     }     assert(n >= 0);     s = std::string(n,' ') + s;     if (s[n+0] == '<' && s[n+1] == '/')     {       n-=2;     }   }   return v; } /* const std::pair<std::string,std::string> ribi::xml::XmlToStr(const std::string& s) {   assert(!s.empty());   assert(s[0] == '<');   assert(s[s.size() - 1] == '>');   assert(s.find('>') != std::string::npos);   const int tag_name_sz = static_cast<int>(s.find('>')) - 1;   const std::string tag_name = s.substr(1,tag_name_sz);   assert(s.find_last_of('/') != std::string::npos);   const int content_sz = static_cast<int>(s.find_last_of('/')) - tag_name_sz - 3;   const std::string content = s.substr(tag_name.size() + 2,content_sz);   const std::pair<std::string,std::string> p { tag_name, content };   assert(ToXml(p.first,p.second) == s);   return p; } */ std::pair<std::string,std::vector<std::string>> ribi::xml::XmlToVector(   const std::string& s) {   assert(!s.empty());   assert(s[           0] == '<');   assert(s[s.size() - 1] == '>');   assert(s.find('>') != std::string::npos);   //Read the name tag   //<tag_name>...</tag_name>   const std::pair<std::string,std::string> p = FromXml(s);   const std::string tag_name = p.first;   std::vector<std::string> content;   //Remove the name tags   //std::string t = s.substr(tag_name_sz + 2,s.size() - (2 * tag_name_sz) - 5);   std::string t = p.second;   for (int i=0; !t.empty(); ++i)   {     //Read the index tags and item     //<index>item</index>     assert(!t.empty());     assert(t[0] == '<');     assert(t[t.size() - 1] == '>');     assert(t.find('>') != std::string::npos);     const int index_tag_sz = static_cast<int>(t.find('>')) - 1;     const std::string index_tag = t.substr(1,index_tag_sz);     #ifndef NDEBUG     if (!CanLexicalCast<int>(index_tag))     {       TRACE("ERROR");       TRACE(t);       TRACE(index_tag);     }     #endif     assert(CanLexicalCast<int>(index_tag));     assert(i == boost::lexical_cast<int>(index_tag));     assert(t.find('/') != std::string::npos);     const int item_sz = static_cast<int>(t.find('/')) - index_tag_sz - 3;     const std::string item = t.substr(index_tag.size() + 2,item_sz);     const int total_sz = (2 * index_tag_sz) + item_sz + 5;     t = t.substr(total_sz,t.size() - total_sz);     content.push_back(item);   }   assert(VectorToXml(tag_name,content) == s);   return std::make_pair(tag_name,content); }