Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(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 (C) 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 (C) 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);
}

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml