Parsing xml

// Copyright 2014 Andrzej Krzemienski.
//
// This demonstrates how to use Boost.PropertyTree
// to read and write xml files.

#include <vector>
#include <string>
#include <fstream>
#include <locale>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/ptree.hpp>

typedef boost::gregorian::date Date;

struct Flight
{
  std::string carrier;
  unsigned number;
  Date date;
  bool cancelled;
};

typedef std::vector<Flight> Sked;

class DateTranslator
{
  typedef boost::date_time::date_facet<Date, char> tOFacet;
  typedef boost::date_time::date_input_facet<Date, char> tIFacet;
  std::locale locale_;
  
  static std::locale isoDateLocale() {
    std::locale loc;
    loc = std::locale( loc, new tIFacet("%Y-%m-%d") );
    loc = std::locale( loc, new tOFacet("%Y-%m-%d") );
    return loc;
  }
  
public:
  typedef std::string internal_type;
  typedef Date external_type;
  
  DateTranslator( ) : locale_( isoDateLocale() ) {}
  
  boost::optional<external_type> get_value(internal_type const& v)
  {
    std::istringstream stream(v);
    stream.imbue(locale_);
    external_type vAns;
    if( stream >> vAns ) {
      return vAns;
    }
    else {
      return boost::none;
    }
  }
  
  boost::optional<internal_type> put_value(external_type const& v)
  {
    std::ostringstream ans;
    ans.imbue(locale_);
    ans << v;
    return ans.str();
  }
};

namespace boost{ namespace property_tree{

  template<> struct translator_between<std::string, Date> {
    typedef DateTranslator type;
  };
}}

Sked read( std::istream & is )
{
  // populate tree structure pt
  using boost::property_tree::ptree;
  ptree pt;
  read_xml(is, pt);
  
  // traverse pt
  Sked ans;
  BOOST_FOREACH(ptree::value_type const&v, pt.get_child("sked")) {
    if( v.first == "flight" ) {
      Flight f;
      f.carrier = v.second.get<std::string>("carrier");
      f.number = v.second.get<unsigned>("number");
      f.date = v.second.get<Date>("date");
      f.cancelled = v.second.get("<xmlattr>.cancelled", false);
      ans.push_back(f);
    }
  }
  return ans;
}

void write( Sked sked, std::ostream & os )
{
  using boost::property_tree::ptree;
  ptree pt;
  
  pt.add("sked.version", 3);
  
  BOOST_FOREACH( Flight f, sked ) {
    ptree & node = pt.add("sked.flight", "");
    node.put("carrier", f.carrier);
    node.put("number", f.number);
    node.put("date", f.date);
    if( f.cancelled ) node.put("<xmlattr>.cancelled", true);
  }
  write_xml(os, pt);
}

int main()
{
  std::ifstream input("input.xml");
  Sked sked = read( input );
  std::ofstream output("output.xml");
  write( sked, output );
  return 0;
}

8 Responses to Parsing xml

  1. marto says:

    Hello,
    please tell me how can I put polish font to xml.
    I using something like this and in xml file there aren’t polish font:

    ptree pt;
    ptree & tresc1 = pt.add("nazwaroota.tresc", "");
    tresc1.put("coś", "źle");
    write_xml("testXml.xml", pt);
    
    • @marto: It may not be that easy to do with Boost.PropertyTree library. Note that it is not designed to parse XML files in every aspect. Being able to parse XML is a side effect. That said, your problem can be solved, but with some additional effort. The thing is that ptree is using std::string inside, and std::string is ASCII-oriented and has no knowledge of non-ASCII characters, you would have to use another string type that is capable of storing non-ASCII characters, e.g., a UNICODE-aware string. ptree is just a typedef on:

      typedef basic_ptree< std::string, std::string > ptree;
      

      If you change it into:

      typedef basic_ptree< utf8_string, utf8_string > ptree;
      

      It should work, provided that you have some sort of utf8_string.

  2. Carlos Cortijo says:

    Try boost::property_tree::wptree instead

  3. Missing a opening brace on line 84

  4. CashCow says:

    The issue I have with your code is that you are adding to namespace boost an overload that does not involve one of your types but instead a class in boost itself, so at some point in the future when they automatically support dates for property trees your code will cause a clash.

    Property Tree functions take a 3rd parameters with a translator so you can just use that when you want to get or put the date. It may also be more efficient as you will want to reuse your DateTranslator (in particular the locale and its facets) rather than have it create a new one each time, which will happen if you use the standard one.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s