previousnext

2. Reading a simple document

Given the simple following document, which contains a structure of some arbitrary solar system. The functionality of xmlio should be demonstrated with this xml document:

solarsystem1.xml:
<solarsystem>
  <star name="sun"/>
  <planet name="merkur"/>
  <planet name="venus"/>
  <planet name="earth"/>
  <planet name="mars"/>
</solarsystem>

We want to write a programme that does some computations on such solar systems. The data should be read from a file, for instance our solarsystem1.xml file. The first example of this just does the following: It reads the file solarsystem1.xml and writes the start tags of all elements to stdout.

solarsystem1.cpp:
#include <vector>
#include <xmlio/XMLIO_Document.h>


/** Contains solar system data. It is derived from XMLIO_Document
    and therefore inherits methods to read xml documents. */
class Solarsystem: public XMLIO_Document {

public:

  /** Called when a start tag of an xml element is found in the xml
      document. Tag and attributes are passed to this function. */
  virtual XMLIO_Element* XMLIO_startTag(const string& tag, XMLIO_Attributes &attrs) {
    /* Just print the name of the tag. */
    fprintf(stdout,"  element found: %s\n",tag.c_str());

    /* The Solarsystem object should receive all data of the element whose
       start tag just has been found. */
    return this;
  }
};



/* Our main programme. */
int main() {
  Solarsystem solarsystem;
  
  /* Open the xml file for reading. */
  solarsystem.open("solarsystem1.xml","r");
  /* Parse file. */
  solarsystem.readDocument();
  /* Close docment. */
  solarsystem.close();
}

Output:
  element found: solarsystem
  element found: star
  element found: planet
  element found: planet
  element found: planet
  element found: planet

XMLIO_Element is the xmlio representation of an xml element. These elements can themself contain other nested elements. The xml document can be understood as one single xml element. It is represented by XMLIO_Document, which is derived from XMLIO_Element. XMLIO_Document objects have the ability to open, read and close files. In our test programme this is done by calling the XMLIO_Document methods open("solarsystem1.xml","r"), readDocument() and close().

The class Solarsystem is derived from XMLIO_Document and is used to handle the xml document's data. While parsing the document, which is automatically done when calling the readDocument() method, the XMLIO_Document object throws events, for instance when it finds start tags, end tags, character data and so on. In our case we are just interested in the start tags and so all we have to do is to overwrite the method XMLIO_startTag(...):

  virtual XMLIO_Element* XMLIO_startTag(const string& tag, XMLIO_Attributes &attrs) {
    fprintf(stdout,"  element found: %s\n",tag.c_str());

    return this;
  }

In this function we simply write all start tags that we receive to stdout. The function returns an XMLIO_Element which should receive all data that belongs to the current element. Returning this means, that all data should still be passed to our Solarsystem object and not be forwarded to any other object.

This was quite simple. But now we want to extend our example by Orb, Star and Planet classes, while Star and Planet are derived from class Orb. Orb represents all orbs in the solar system (stars, planets, ...). It is derived from XMLIO_Element since all orbs should be constructed by xml elements of our solarsystem1.xml file. At the moment we have just included the name of the orb as extra information (attribute name="..."). The type of the orb can be extracted from the start tag, which can either be star or planet. So Orb, Star and Planet objects are constructed by passing the start tag and the attributes to the constructor of these objects.

The XMLIO_startTag(...) method of the Solarsystem class is modified. It now takes care of what kind of element is found in the document. In case of star or planet elements a new Star resp. Planet object is constructed and added to the stars resp. planets vector. All data belonging to these elements should be passed to the newly created objects, which is achieved by returning these objects instead of return this;. That's it.

solarsystem2.cpp:
#include <vector>
#include <xmlio/XMLIO_Document.h>


/** Contains data of an orb (star, planet, ...). */
class Orb: public XMLIO_Element {

public:
  
  /* Constructor. Gets orb type from tag and name from attributes. */
  Orb(const string& tag, XMLIO_Attributes & attrs) {
    orb_type = tag;
    name     = attrs["name"];
  };

  /* Prints orb information to stdout. */
  void print() {
    fprintf(stderr,"  %s: %s\n",orb_type.c_str(),name.c_str());
  }

  string orb_type;
  string name;
};


/* Contains data of a star. */
class Star: public Orb {

public:
  
  /* Constructor. */
  Star(const string& tag, XMLIO_Attributes & attrs):
    Orb(tag,attrs) {}

};


/* Contains data of a planet. */
class Planet: public Orb {

public:
  
  /* Constructor. */ 
  Planet(const string& tag, XMLIO_Attributes & attrs):
    Orb(tag,attrs) {}

};



/** Contains solar system data. It is derived from XMLIO_Document
    and therefore inherits methods to read xml documents. */
class Solarsystem: public XMLIO_Document {

public:

  /** Destructor. Clean up memory. */
  ~Solarsystem() {

    vector<Star*>::iterator star;
    vector<Planet*>::iterator planet;

    for (star = stars.begin(); star != stars.end(); ++star)
      delete *star;

    for (planet = planets.begin(); planet != planets.end(); ++planet)
      delete *planet;
  }



  /** Called when a start tag of an xml element is found in the xml
      document. Tag and attributes are passed to this function. */
  virtual XMLIO_Element* XMLIO_startTag(const string& tag, XMLIO_Attributes &attrs) {

    if (tag == "star") {
      Star* star = new Star(tag,attrs);
      stars.push_back(star);

      /* Pass all data belonging to the star element to the
	 star object. */
      return star;
    }

    if (tag == "planet") {
      Planet* planet = new Planet(tag,attrs);
      planets.push_back(planet);

      /* Pass all data belonging to the planet element to the
	 planet object. */
      return planet;
    }

    /* The Solarsystem object should receive all data of the element whose
       start tag just has been found. */
    return this;
  }

  /* Prints solarsystem information to stdout. */
  void print() {
    unsigned int i;

    fprintf(stderr,"Our solarsystem has %d stars:\n",(int) stars.size());
    for (i = 0; i < stars.size(); ++i)
      stars[i]->print();

    fprintf(stderr,"and %d planets:\n",(int) planets.size());
    for (i = 0; i < planets.size(); ++i)
      planets[i]->print();
  }

  vector stars;
  vector planets;
};



/* Our main programme. */
int main() {
  Solarsystem solarsystem;
  
  /* Open the xml file for reading. */
  solarsystem.open("solarsystem1.xml","r");
  /* Parse file. */
  solarsystem.readDocument();
  /* Close docment. */
  solarsystem.close();

  /* Prints the solarsystem data. */
  solarsystem.print();
}

Output:
Our solarsystem has 1 stars:
  star: sun
and 4 planets:
  planet: merkur
  planet: venus
  planet: earth
  planet: mars

This example shows how we can build up a solar system data structure from the given xml file. More of the xmlio functionality will be explained in the next lessons. I'll be working on that... Peter

previousnext