Wednesday, 3 March 2010

I wanted an iomanipulator to escape XML

...and so began a steep learning curve. I had some strings to insert into XML being output on the cout stream, and ideally I would simply like to do something like this:
string myXml("xml&<>");
cout<<escapexml<<myXml;

..and the output should be 'escaped'.
Of course I could have made a function and simply transformed one string into another, but I was also keen to learn about the iostream framework. Here are a few ways which iterate towards the final goal
1. 'Adapt' the string
Custom inserters can be made for a given class, so by wrapping the string in a class, I cold simply make an inserter for that class:
Here is the header:

class Wrapper{

std::string &m_data;

public:

typedef std::string::value_type value_type;Font size

Wrapper(std::string & data):m_data(data){ }

std::string &str(){

return m_data;

}

};


std::ostream &

operator << (std::ostream & os, Wrapper & s);


...and the definition for that inserter:

std::ostream &

operator<< (std::ostream & os, Wrapper & s){

XmlEscape x(os);

for_each(s.str().begin(),s.str().end(),x);

return os;

}


where XmlEscape is a functor:


class XmlEscape{

static const char * m_chars;

static const char * m_replacements[];

static const unsigned int m_nchars=5;

std::ostream & m_os;

public:

XmlEscape(std::ostream & os);

void operator()(char c);

std::ostream & ostream();

};


with implementation:


const char * XmlEscape::m_chars ="&\'\"><";

const char * XmlEscape::m_replacements[]={"&amp;", "&apos;", "&quot;", "&gt;", "&lt;"};


XmlEscape::XmlEscape(std::ostream & os):m_os(os){

//nop

}


void

XmlEscape::operator()(char c){

unsigned int i(m_nchars);

while(i--){

if (c==m_chars[i]) {

m_os<<m_replacements[i];

return;

}

}

m_os<<c;

}

2. Adapt the stream




No comments: