Click here to Skip to main content
12,624,429 members (38,253 online)
Click here to Skip to main content

Stats

152.7K views
4.1K downloads
59 bookmarked
Posted

Simple C++ class for XML writing

, 4 Dec 2003
Tiny template-based C++ class, which simplifies writing of XML data.
//////////////////////////////////////////////////////////////////////
//
// xmlparser.h: interface & implementation for the XmlStream class.
// 
// Author: Oboltus, December 2003
//
// This code is provided "as is", with absolutely no warranty expressed
// or implied. Any use is at your own risk.
//
//////////////////////////////////////////////////////////////////////

#ifndef __XMLWRITER_H_2CC1D410_9DE8_4452_B8F0_F987EF152E06
#define __XMLWRITER_H_2CC1D410_9DE8_4452_B8F0_F987EF152E06

// disable terrible MSVC warnings which are issued when using STL
#ifdef	_MSC_VER
#pragma warning( disable : 4786 ) 
#pragma warning( disable : 4514 )
#endif

#include	<stack>
#include	<string>
#include	<sstream>

namespace xmlw {

class XmlStream {
public:

	// XML version constants
	enum {versionMajor = 1, versionMinor = 0};

	// Internal helper class
	struct Controller {
		typedef	enum {whatProlog, whatTag, whatTagEnd, whatAttribute, whatCharData}	what_type;

		what_type	what;
		std::string str;

		inline Controller(const Controller& c) : what(c.what), str(c.str) {}
		inline Controller(const what_type _what) : what(_what){}
		
		// use template constructor because string field <str> may be initialized 
		// from different sources: char*, std::string etc
		template<class t>
		inline Controller(const what_type _what, const t& _str) : what(_what), str(_str) {}
	};

	// XmlStream refers std::ostream object to perform actual output operations
	inline XmlStream(std::ostream&	_s) : s(_s), state(stateNone), prologWritten(false) {}

	// Before destroying check whether all the open tags are closed
	~XmlStream() {
		if (stateTagName == state) {
			s << "/>";
			state = stateNone;
		}
		while (tags.size())
			endTag(tags.top());
	}

	// default behaviour - delegate object output to std::stream
	template<class t>
	XmlStream& operator<<(const t& value) {
		if (stateTagName == state)
			tagName << value;
		s << value;
		return *this;
	}

	// this is the main working horse
	// and it's long a little
	XmlStream& operator<<(const Controller& controller) {

		switch (controller.what) {
		case Controller::whatProlog:
			if (!prologWritten && stateNone == state) {
				s << "<?xml version=\"" << versionMajor << '.' << versionMinor << "\"?>\n";
				prologWritten = true;
			}
			break;	//	Controller::whatProlog

		case Controller::whatTag:
			closeTagStart();
			s << '<';
			if (controller.str.empty()) {
				clearTagName();
				state = stateTagName;
			}
			else {
				s << controller.str;
				tags.push(controller.str);
				state = stateTag;
			}
			break;	//	Controller::whatTag

		case Controller::whatTagEnd:
			endTag(controller.str);
			break;	//	Controller::whatTagEnd

		case Controller::whatAttribute:
			switch (state) {
			case stateTagName:
				tags.push(tagName.str());
				break;

			case stateAttribute:
				s << '\"';
			}

			if (stateNone != state) {
				s << ' ' << controller.str << "=\"";
				state = stateAttribute;
			}
			// else throw some error - unexpected attribute (out of any tag)

			break;	//	Controller::whatAttribute

		case Controller::whatCharData:
			closeTagStart();
			state = stateNone;
			break;	//	Controller::whatCharData
		}

		return	*this;
	}

private:
	// state of the stream 
	typedef	enum {stateNone, stateTag, stateAttribute, stateTagName}	state_type;

	// tag name stack
	typedef std::stack<std::string>	tag_stack_type;

	tag_stack_type	tags;
	state_type	state;
	std::ostream&	s;
	bool	prologWritten;
	std::ostringstream	tagName;

	// I don't know any way easier (legal) to clear std::stringstream...
	inline void clearTagName() {
		const std::string	empty_str;
		tagName.rdbuf()->str(empty_str);
	}

	// Close current tag
	void closeTagStart(bool self_closed = false) {
		if (stateTagName == state)
			tags.push(tagName.str());

		// note: absence of 'break's is not an error
		switch (state) {
		case stateAttribute:
			s << '\"';

		case stateTagName:
		case stateTag:
			if (self_closed)
				s << '/';
			s << '>';
		}
	}

	// Close tag (may be with closing all of its children)
	void endTag(const std::string& tag) {
		bool	brk = false;

		while (tags.size() > 0 && !brk) {
			if (stateNone == state)
				s << "</" << tags.top() << '>';
			else {
				closeTagStart(true);
				state = stateNone;
			}
			brk = tag.empty() || tag == tags.top();
			tags.pop();
		}
	}
};	//	class XmlStream

// Helper functions, they may be simply overwritten
// E.g. you may use std::string instead of const char*

inline const XmlStream::Controller prolog() {
	return XmlStream::Controller(XmlStream::Controller::whatProlog);
}

inline const XmlStream::Controller tag() {
	return XmlStream::Controller(XmlStream::Controller::whatTag);
}

inline const XmlStream::Controller tag(const char* const tag_name) {
	return XmlStream::Controller(XmlStream::Controller::whatTag, tag_name);
}

inline const XmlStream::Controller endtag() {
	return XmlStream::Controller(XmlStream::Controller::whatTagEnd);
}

inline const XmlStream::Controller endtag(const char* const tag_name) {
	return XmlStream::Controller(XmlStream::Controller::whatTagEnd, tag_name);
}

inline const XmlStream::Controller attr(const char* const attr_name) {
	return XmlStream::Controller(XmlStream::Controller::whatAttribute, attr_name);
}

inline const XmlStream::Controller chardata() {
	return XmlStream::Controller(XmlStream::Controller::whatCharData);
}

}	// namespace

#endif  //  __XMLWRITER_H_2CC1D410_9DE8_4452_B8F0_F987EF152E06

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Oboltus
Web Developer
Russian Federation Russian Federation
Programming since 1992
Favorite programming language: C++
Main interest (currently): computer modeling of physical processes in radiation-damaged metalls

You may also be interested in...

Pro
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161128.1 | Last Updated 5 Dec 2003
Article Copyright 2003 by Oboltus
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid