Implementing IXmlWriter Part 1: The Basics
Implementing IXmlWriter c++ ixmlwriter xml
Published: 2005-09-30
Implementing IXmlWriter Part 1: The Basics

This is part 1/14 of my Implementing IXmlWriter post series.

After writing my blog post Don’t Form XML Using String Concatenation, I realized that writing a C++ System.Xml.XmlWriter workalike involves some interesting challenges. Therefore, I’ve decided to write a series of blog posts about building a streaming C++ XML generator, a.k.a. IXmlWriter, step-by-step. For this series, I will follow the practice of test-driven development and write a test case followed by an implementation which passes the test case. Future posts' test cases will be constructed to illustrate bugs in or new features desired from the previous post’s implementation. The test cases will be constructed with the goal of having IXmlWriter be as similar to System.Xml.XmlWriter as possible.

This is the first post in that series. Today’s test case is:

1
2
3
4
5
6
7
8
9
StringXmlWriter xmlWriter;
xmlWriter.WriteStartElement("root");
  xmlWriter.WriteStartElement("element");
    xmlWriter.WriteString("Contents");
  xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();

std::string strXML = xmlWriter.GetXmlString();
// strXML should be <root><element>Contents</element></root>

Looking carefully at this test case, I see the following requirements for StringXmlWriter:

  1. It needs to support 4 functions: WriteStartElement(), WriteEndElement(), WriteString(), and GetXmlString().
  2. It must provide the ability to retrieve the XML as a std::string. For simplicity, I will simply keep the generated XML as a std::string member variable and return it from GetXmlString().
  3. It needs to keep track of what XML elements have been opened so that it can properly produce the end tag when WriteEndElement() is called. It is sensible for StringXmlWriter to contain this logic and not the caller (in other words, WriteEndElement() shouldn’t take the name of the element to close) because it doesn’t make sense for WriteEndElement() to close any other element other than the most recently opened one. The natural data structure to store this information is a stack, so I will use a std::stack.

Remember, I am only concerned with passing this test case. To fix any bugs or deficiencies I must write a new test case first, and then fix the implementation accordingly. Keeping this in mind, here is the overly simple implementation which I came up with to pass this test case:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class StringXmlWriter
{
private:
    std::stack<std::string> m_openedElements;
    std::string m_xmlStr;

public:
    void WriteStartElement(const std::string& localName)
    {
        m_openedElements.push(localName);
        m_xmlStr += '<';
        m_xmlStr += localName;
        m_xmlStr += '>';
    }

    void WriteEndElement()
    {
        std::string lastOpenedElement = m_openedElements.top();
        m_xmlStr += "</";
        m_xmlStr += lastOpenedElement;
        m_xmlStr += '>';
        m_openedElements.pop();
    }

    void WriteString(const std::string& value)
    {
        m_xmlStr += value;
    }

    std::string GetXmlString() const
    {
        return m_xmlStr;
    }
};