(C++) Xml
January 25, 2018 · View on GitHub
(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.
- boost::property_tree
- RapidXML
References
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); }