XML XSLT XPath DOCDocument

This is one of the ways that I create dynamic web pages on the fly with using XML/XSLT with a PHP back-end to update the XML on the fly with data obtained (either from MySQL etc, but in this example it will be hard coded).

To start with I am using the PHP DOMDocument object, which is a very good XML reader and updater that uses the XPath query to obtain nodes(lists) from within the XML document itself. So to start with I first load up the XML file that has the basic outputs defined, which means that if you did not want to insert any data on the fly, you could use this basic one.

<?php 
    $xmlDoc = new DOMDocument();
    $xmlDoc->load("ass1baseoutput.xml");

and here is the XML file, what it basically is is just a symbols that I have created on the fly to mean something to me, like “title”,”form” so that I know what I kinder should be doing with the data located within them, so as you can probably tell I am about to generate an form filled with data.

<root>
	<title>XML/XSLT test</title>
	<form>
		<table>
			<row>
				<td>value1</td>
				<td>value2</td>
			</row>
			<row>
				<td>value3</td>
				<td>value4</td>
			</row>
		</table>
	</form>
</root>

The next this to do, is now that we have read in the XML file, we can use the DOCDocument to either createElement, which we can appendChild to that element of any data that we want to insert on the file, I am using a XPath to find the place where I want to insert the new data, in this case within the root/form/table element of the XML document. Since within the above document we have a row tag which is followed by a td tag, I have to create the same to insert, which is why I am creating a element first of a “row” and then appending a child (the 2 td elements) to that node, which at the end I insert the created row into the document via the xpath result.

// now that you have a XML document loaded, if you wanted to add more to areas, then you are 
// able to, thus changing the output on the fly as such.
 
	$xpath = new DOMXpath($xmlDoc);
	$nodelist = $xpath->query('/root/form/table');
	$row = $xmlDoc->createElement("row","");
	$row->appendChild($xmlDoc->createElement("td","Created on the fly"));
	$row->appendChild($xmlDoc->createElement("td","Right hand side"));
	$nodelist->item(0)->appendChild($row);
 
    $xslDoc = new DOMDocument();
    $xslDoc->load("sample.xsl");

Here is my XSLT file, how it kinder works is within the manor of matching elements from the XML document into the stylesheet (XSLT) so for example means to match from the base node (in this case the node called ROOT), then create some textural output (which is html code) and then pull back from data from the /root/title element from within the XML document with using the , to apply other templates that may match other parts from within the XML document, you use the adaptly named apply-templates 🙂 as

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" encoding="iso-8859-1" indent="no"/>
<xsl:template match="/">
  <html>
  <title><xsl:value-of select="/root/title"/></title>
  <body>
	<xsl:apply-templates select="/root/form"/>
</body>
</html>
</xsl:template>
 
<xsl:template match="/root/form">
	<form action="" method="">
		<xsl:apply-templates select="/root/form/table"/>
		<input type="submit" value="submit"/>
	</form>
</xsl:template>
 
<xsl:template match="/root/form/table">
	<table>
		<xsl:for-each select="row">
		<tr>
				<xsl:for-each select="td">
					<td><xsl:value-of select="."/></td>
				</xsl:for-each>
		</tr>
		</xsl:for-each>
	</table>
</xsl:template>
 
</xsl:stylesheet>

and now all we need to do is to process the XML/XSLT to produce the output, with using the XSLTProcessor class

    $proc = new XSLTProcessor();
 
    $proc->importStylesheet($xslDoc);
    echo $proc->transformToXML($xmlDoc);
?>

The output would be, which has the inserted data “Created on the fly”, it may seem like a allot of hassle to get the output like below, but if you was using a far bigger XML file and XSLT with also creating allot of different pages with the same data, but just inserting different parts into the correct XML area, you can start to see that it will save allot of time and also be a nicely validated document.

<html><title>XML/XSLT test</title><body><form action="" method=""><table>
<tr><td>value1</td><td>value2</td></tr>
<tr><td>value3</td><td>value4</td></tr>
<tr><td>Created on the fly</td><td>Right hand side</td></tr>
</table><input type="submit" value="submit"></form></body></html>

SOAP – the client with requested and responses

From the SOAP server page here, this is the client that can connect to the PHP server page, the client uses the WSDL (Web Services Description Language) part of the server to find out the functions that are available.

To connect to the server WSDL, you create a new Soap Client with the WSDL as the URI link

  $client = new SoapClient("http://localhost/projects/webservice/zend_soap_server.php?wsdl")

in this example I am passing in some variables for debugging

    array(
      "trace"      => 1,		// enable trace to view what is happening
      "exceptions" => 0,		// disable exceptions
      "cache_wsdl" => 0) 		// disable any caching on the wsdl, encase you alter the wsdl server

so to only get a response from the server you just need to call the function on the server like it is running within the local PHP environment

  echo $client->getQuote("monday");

What is happening behind the scenes, is that the client has created a XML request and send that to the server, the server processes the request and responds with a XML response to the client, the client then pulls out the result from the XML response, here is how with using the soap client trace debugging out

  // display what was sent to the server (the request)
  echo "<p>Request :".htmlspecialchars($client->__getLastRequest()) ."</p>";
  // display the response from the server
  echo "<p>Response:".htmlspecialchars($client->__getLastResponse())."</p>";

here is the full source code, save as zend_soap_client.php (it is not using any zend framework, but the server is)

<?php
  // create a connection to the local host zend soap server, pull back the wsdl to get the functions names
  // and also the parameters and return values
  $client = new SoapClient("http://localhost/projects/webservice/zend_soap_server.php?wsdl",
    array(
      "trace"      => 1,		// enable trace to view what is happening
      "exceptions" => 0,		// disable exceptions
      "cache_wsdl" => 0) 		// disable any caching on the wsdl, encase you alter the wsdl server
  );
 
  // get a response from the WSDL zend server function getQuote for the day monday
  echo $client->getQuote("monday");
 
  // display what was sent to the server (the request)
  echo "<p>Request :".htmlspecialchars($client->__getLastRequest()) ."</p>";
  // display the response from the server
  echo "<p>Response:".htmlspecialchars($client->__getLastResponse())."</p>";
?>

and here is the output on the web page

Monday's child is fair of face
 
Request :<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns1="http://localhost/projects/webservice/zend_soap_server.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><ns1:getQuote><quote xsi:type="xsd:string">monday</quote></ns1:getQuote></SOAP-ENV:Body></SOAP-ENV:Envelope>
 
Response:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns1="http://localhost/projects/webservice/zend_soap_server.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><ns1:getQuoteResponse><return xsi:type="xsd:string">Monday's child is fair of face</return></ns1:getQuoteResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

SOAP – Server

SOAP is the acronym for Simple Object Access Protocol, it uses the web and XML for a client to find out what functions are available on a server using the WSDL (Web Server Description Language) (this is also in XML format) and once the client knows what functions are available and also what parameters can be passed, the client is then able to call the function and gain the response from the server.

I am using the Zend Framework to auto generate the WSDL file since there does not appear to be at present a function within the PHP standard framework to create one, I have included below how to install the Zend framework. Once you have installed the framework you need to either alter the php.ini file to tell it where the framework is or within the .php file for your server include details on where the framework is within that and include the relevant files.

Below I have told the php runtime within the .php file where the zend library framework is by using ini_setup include path settings.

/* setup the including path for the zend library framework */
ini_set('include_path', '/usr/share/php/libzend-framework-php/');
/* and include the zend soap files */
require_once 'Zend/Soap/AutoDiscover.php';
require_once "Zend/Soap/Server.php";

with including the auto discover (the WSDL generator part of the zend framework that we are wanting) and the server php files.

For the auto discover to work you need to use the php doc notation so that the WSDL can be generated correctly, so for example if you was using a function that was taking in a string and returning a string as in the main example below, you would use something like this (where the php doc is /** start)

  /**
   * @param string $quote
   * @return string
  */
  function getQuote($quote) {

To generate WSDL file I am using the parameter within the URL (?wsdl) and thus if that is present auto generate the WSDL file else act as the server, so

/* if the client is requesting the WSDL file (the web service definition language) */
if(isset($_GET['wsdl'])) {
   // use the zend soap autodiscover function to create the wsdl file from using the phpdoc info from the class QuoteOfTheDay */
    $autodiscover = new Zend_Soap_AutoDiscover();
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();

with using the Zend_Soap_AutoDiscover class and then setting up the class that you want to expose to the WSDL generator / server and then output (handle) the WSDL request.

Here is the full code

<?php
 
/* setup the including path for the zend library framework */
ini_set('include_path', '/usr/share/php/libzend-framework-php/');
/* and include the zend soap files */
require_once 'Zend/Soap/AutoDiscover.php';
require_once "Zend/Soap/Server.php";
 
/* this is the class to be *expose* to the SOAP interface */
class QuoteOfTheDay {
 
  /* the quotes to be used from within the function getQuote */
  private $quotes = array("monday" => "Monday's child is fair of face", "tuesday"=>"Tuesday's child is full of grace","wednesday" =>"Wednesday's child is full of woe",
  "thursday" =>"Thursday's child has far to go", "friday" => "Friday's child is loving and giving", "saturday" =>"Saturday's child works hard for a living",
  "sunday" =>"But the child who is born on the Sabbath Day - Is bonny and blithe and good and gay");  
 
/* you have to use the phpDoc format for the zend soap auto discover to pick up the parameters and return types */
  /**
   * @param string $quote
   * @return string
  */
  function getQuote($quote) {
    /* just encase the string is in uppercase*/
    $symbol = strtolower($quote);
    /* if there is a quote for the day requested */
    if (isset($this->quotes[$quote])) {
      return $this->quotes[$quote];
    } else {
      /* else error */
      throw new SoapFault("Server","Unknown Symbol '$quote'.");
    }
  }
}
 
/* if the client is requesting the WSDL file (the web service definition language) */
if(isset($_GET['wsdl'])) {
   // use the zend soap autodiscover function to create the wsdl file from using the phpdoc info from the class QuoteOfTheDay */
    $autodiscover = new Zend_Soap_AutoDiscover();
    $autodiscover->setClass('QuoteOfTheDay');
    $autodiscover->handle();
} else {
  // otherwise I am the server in question, setup a new soap server with the a handle on the class QuoteOfTheDay 
    $soap = new Zend_Soap_Server("http://localhost/projects/webservice/zend_soap_server.php?wsdl"); // this current file here
    $soap->setClass('QuoteOfTheDay');
    $soap->handle();
}
?>

Save that as the zend_soap_server.php within your local PHP web serving directory you need to have the zend framework installed, below may help (within the php.ini there will either a extension list normally within a Windows setup)

in Linux (Ubuntu/Kubuntu etc) I installed the zend frame work for php by

aptitude install libzend-framework-php

or on windows this URL may be of use ? installing zend framework on windows

This is the output of the XML for the WSDL generation, it would be the output of a URL similar to http://localhost/zend_soap_server.php?wsdl

<definitions name="QuoteOfTheDay" targetNamespace="http://localhost/projects/webservice/zend_soap_server.php">
-
<types>
<xsd:schema targetNamespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</types>
-
<portType name="QuoteOfTheDayPort">
-
<operation name="getQuote">
<documentation>@param string $quote</documentation>
<input message="tns:getQuoteIn"/>
<output message="tns:getQuoteOut"/>
</operation>
</portType>
-
<binding name="QuoteOfTheDayBinding" type="tns:QuoteOfTheDayPort">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
-
<operation name="getQuote">
<soap:operation soapAction="http://localhost/projects/webservice/zend_soap_server.php#getQuote"/>
-
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</input>
-
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost/projects/webservice/zend_soap_server.php"/>
</output>
</operation>
</binding>
-
<service name="QuoteOfTheDayService">
-
<port name="QuoteOfTheDayPort" binding="tns:QuoteOfTheDayBinding">
<soap:address location="http://localhost/projects/webservice/zend_soap_server.php"/>
</port>
</service>
-
<message name="getQuoteIn">
<part name="quote" type="xsd:string"/>
</message>
-
<message name="getQuoteOut">
<part name="return" type="xsd:string"/>
</message>
</definitions>

XML reader compile and link – cpp

From the post of the full code of the xml reader project, full code here.

Here is how to compile it up and also link to a basic main run method.

Compile up

g++ cppxmlreader.cpp -c

will create the cppxmlreader.o (which is the object file) if you do file on the object the outout would be

file cppxmlreader.o
cppxmlreader.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

and then using this code xmlreadmain.cpp

#include "cppxmlreader.h"
 
int main(int argv, char** argc)
{
    xmlReader xmlRead;
    xmlRead.loadFile("words.xml");
    xmlRead.printOuterXML();
    return 0;
}

This will include the header file of the cppxmlreader.h and then create a xmlReader object and load a basic file of words.xml and then print out the xml read in.

Here is the words.xml

<?xml version="1.0" encoding="UTF-8"?>
<words>
  <word attname="hi">merry</word>
  <word>old</word>
  <word>sole</word>
  <word>was</word>
  <word>he</word>
</words>

and to compile up the main run the cppxmlreader.o is the object file from the above compiling and the xmlreadmain.cpp is the main runner with -o being the output name of the executable.

g++ xmlreadmain.cpp cppxmlreader.o -o xmlreader

and if you run the xmlreader, the output should be

XML Reader Main Object (Xml main details)
XML Object
Tagname  :
Tagvalue :
Attribute 0 : Name : version Value : 1.0
Attribute 1 : Name : encoding Value : UTF-8
XML Reader xml details
XML Object
Tagname  :words
Tagvalue :
XML Object
Tagname  :word
Tagvalue :merry
Attribute 0 : Name : attname Value : hi
XML Object
Tagname  :word
Tagvalue :old
XML Object
Tagname  :word
Tagvalue :sole
XML Object
Tagname  :word
Tagvalue :was
XML Object
Tagname  :word
Tagvalue :he
XML Object
Tagname  :/words
Tagvalue :

XML reader full – cpp

Here is the full code for the header and class file from the xml reader project

Here is the cppxmlreader.h file.

#ifndef CPPXMLREADER_H
#define CPPXMLREADER_H
 
#include <string>
#include <vector>
 
// using the namespace std for the string type, vector
using namespace std;
 
const int CHARACTERLENGHT = 80;
const string BADXMLVERSION = "Xml version - first line ? - problem";
const string BADTAGNAME = "Tag name was not present";
const string BADXMLTAGEND = "End tag is not the same as tag name";
const string BADATTRIBUTE = "Attribute in wrong format attributeName=\"attributeValue\"";
 
struct xmlAttribute {
    string _attributeName, _attributeValue;
};
 
class xmlObject {
  private :
    string _tagName, _tagValue;
    vector<xmlAttribute> _attributes;
    bool _xmlMainDetails;
 
  public:
      xmlObject() { _xmlMainDetails = false;} ;
      xmlObject(string tag, string tValue, xmlAttribute attribute);
 
      void setTagName(string tagName);
      void setTagValue(string tagValue);
      void addAttributes(xmlAttribute attribute);
      void setAttributeVector(vector<xmlAttribute> setAtt);
      void setXmlMainDetails(bool value);
      bool getXmlMainDetails();
      void printOutXmlObject();
};
 
class xmlReader {
  protected:
    vector<xmlObject> _xmlMainDetails;
    vector<xmlObject> _xmlDetails;
 
  public: 
    xmlReader();
 
    // open file and read in the xml file and place into the _xmlDetails
    bool loadFile(string filename);
    void printOuterXML();
 
  private :
    xmlObject readLine(string xmlToSplitUp, string* tagName);
    string readUntilCharacter(string line, char characterStart, char characterEnd, string *returnLine);
    xmlAttribute getAttribute(string attributeString);
    vector<xmlAttribute> getAttributesFromString(string str);
};
 
 
#endif // CPPXMLREADER_H

Here is the cppxmlreader.cpp file

#include "cppxmlreader.h"
#include <iostream>
#include <string.h>
#include <fstream>
 
using namespace std;
 
/* xmlObject */
// constructor for xmlObject, if any details are passed whilst constructing 
xmlObject::xmlObject(string tag, string tValue, xmlAttribute attribute)
{
  _tagName = tag;
  _tagValue = tValue;
  _attributes.push_back(attribute);
}
 
// xml <tagname attributes="attributesvalue">VALUE</tagname>
// set the tag name
void xmlObject::setTagName(string tagName)
{
  _tagName = tagName;
}
 
// set tag value
void xmlObject::setTagValue(string tagValue)
{
  _tagValue = tagValue;
}
 
// add attributes to the vector attributes variable
void xmlObject::addAttributes(xmlAttribute attribute)
{
  _attributes.push_back(attribute);
}
 
// fill in the vector attributes variable.
void xmlObject::setAttributeVector(vector<xmlAttribute> setAtt)
{
  _attributes = setAtt;
}
 
// print out the xml object detais, with the attributes values.
void xmlObject::printOutXmlObject()
{
  cout << "XML Object" << endl;
  cout << "Tagname  :" << _tagName << endl;
  cout << "Tagvalue :" << _tagValue << endl;
  for (int i= 0; i < (int)_attributes.size(); i++)
  {
      cout << "Attribute " << i << " : Name : "<< _attributes.at(i)._attributeName << " Value : " << _attributes.at(i)._attributeValue << endl;
  }
}
 
// set the main set details value
void xmlObject::setXmlMainDetails(bool value)
{	
    _xmlMainDetails = value;
}
 
// get a boolean value to see if the xmlObject is the main <?xml .. ?> value
bool xmlObject::getXmlMainDetails()
{
  return _xmlMainDetails;
}
 
 
/*xmlReader*/
xmlReader::xmlReader()
{
}
 
// attribute is normally in the format of attributeName="attributeValue"
xmlAttribute xmlReader::getAttribute(string attributeString)
{
  xmlAttribute returnAttribute;
 
  // make sure that there is a = in the attribute string
  int findEqual = attributeString.find('=');
  if (findEqual > 0)
  {
    // set the attribute name to the substring till the equal
    returnAttribute._attributeName = attributeString.substr(0,findEqual);
    // make sure that there is some characters after the '=' sign.
    if (attributeString.length() > (findEqual+3))
    {
      returnAttribute._attributeValue = attributeString.substr(findEqual+2,(attributeString.length() - (findEqual +3)));
    }
    else
      throw BADATTRIBUTE;
  }else
    // if there does not appear to be ="" at the end of the string then throw a error.
    throw BADATTRIBUTE;
 
  return returnAttribute;
}
 
vector<xmlAttribute> xmlReader::getAttributesFromString(string str)
{
    vector<xmlAttribute> returnAtt;
    xmlAttribute attribute;
    int args;
    char st1[CHARACTERLENGHT];
 
    // args normally equals 1 because there is a attribute present
    // else there was no attribute there, just do one at a time
    args = sscanf(str.c_str(), "%s", st1);
    while (args  == 1 && (str.length() > 1)) {
      // see if there is a real attribute attributeName="attributeValue"
      try {
	attribute = getAttribute(st1);
	// push back in the vector array the attribute
	returnAtt.push_back(attribute);
      } catch (string errorStr)		// any errors
      {
	cout << "ERROR : " << errorStr << endl;
      }
      // re-do the string to pull out any more attributes.
      str = str.substr(strlen(st1));
      // see if there is any more attributes present.
      args = sscanf(str.c_str(), "%s", st1);
    }
    return returnAtt;
}
 
// scan through the xml string and pull out the tags and the attributes and value.
xmlObject xmlReader::readLine(string xmlToSplitUp, string* tagName)
{
  xmlObject returnObj;
  string returnLine, value, endTagName;
  int findXml;
 
  // pick out the tag name, if none then return and throw a bad tag name error.
  *tagName = readUntilCharacter(xmlToSplitUp, '<','>', &returnLine);
  if (tagName->length() ==0)
  {
     throw BADTAGNAME;
     return returnObj;
  }
 
  // if there is a xml version etc in the tagname then process the xml version encoding values.
  findXml=tagName->find("xml");
  if ((findXml > 0 && findXml < tagName->length()) && tagName->length() > 1 )
  {
    // this is the xml version etc.
    // there should be ? at each end of the xml version statement
    string xmlStr = readUntilCharacter(*tagName, '?','?', &returnLine);
    if (returnLine != "?") 
    {
      throw BADXMLVERSION;
      return returnObj;
    }
    // go passed the xml characters.
    returnLine = xmlStr.substr(findXml+3);
    // read any of the attributes from the string
    returnObj.setAttributeVector(getAttributesFromString(returnLine));
    // I am storing the version and any other xml details, so set the return value to store in the correct place.
    returnObj.setXmlMainDetails(true);
  }else if (tagName->length() > 1) 
  {
    // need to see if there is any attributes
    int findTagAtts = tagName->find(' ');
    if (findTagAtts < tagName->length())
    {
      // the attributes are passed the space character in the tagName variable
      string attributes = tagName->substr(findTagAtts);
      // store only the tagName in the tagName variable since pulled out the attributes
      *tagName = tagName->substr(0,findTagAtts);
      // get the attributes into a vector and store in the return object
      returnObj.setAttributeVector(getAttributesFromString(attributes));
    }
 
    if (returnLine.length() > 1)
    {
      // pull out the value in the xml line <tagname>VALUE</tagname>
      value = readUntilCharacter(returnLine,'>','<',&returnLine);
      returnObj.setTagValue(value);
    }
    if (returnLine.length() > 1)
    {
      // pick out the end tag name and make sure it is the same as the first one.
      endTagName = readUntilCharacter(returnLine,'<','>',&returnLine);
      string compareEndTag = "/"+*tagName;
      //if the end tag is not the same as the tag name then throw a error.
      if (endTagName != compareEndTag) 
      {
	throw BADXMLTAGEND;
      } 
    }
    returnObj.setTagName(*tagName);
 
   }
  return returnObj;
}
 
// pick out the characters between two character points, and also return the rest of the line.
string xmlReader::readUntilCharacter(string line, char characterStart, char characterEnd, string *returnLine)
{
  string returnString;
  // find the first occurrence of the character integer placement
  int firstChar = line.find(characterStart);
  // if there is one.
  if (firstChar >= 0)
  {
    // setup the return string, even if a second part cannot be found.
    returnString = line.substr(firstChar+1, (line.length()- firstChar)-1);
    int secChar = returnString.find(characterEnd);
    //if the secound part can be found
    if (secChar > 0)
    {
      *returnLine = returnString.substr(secChar, (returnString.length() - secChar));
      returnString = returnString.substr(0,secChar);
    }
  }
  return returnString;
}
 
// read in the XML file and place each line into the vector xmlObject 
bool xmlReader::loadFile(string filename)
{
  xmlObject xmlObj;
  string line, tagName;
 
  ifstream xmlfile(filename.c_str());
  if (xmlfile.is_open())
  {
      // if the xml version and also the encodingvalues are present.
      //getline(xmlfile,line);
 
      while (!xmlfile.eof())
      {
	  // pull out the start tag and compare against the endtag
	  getline(xmlfile,line);
	  try 
	  {
	    // pick out the xml details from line and return a xmlObject 
	    // to add to the vector array of xml objects
	    // also return the tagName if any futher processing is required.
	    xmlObj = readLine(line, &tagName);
	    // if there is ?xml version etc details present store, else store into the main xml details
	    if (xmlObj.getXmlMainDetails())
	    {
	      _xmlMainDetails.push_back(xmlObj);
	    }
	    else
	    {  
	      _xmlDetails.push_back(xmlObj);
	    }
	  }
	  // if any error occur during the reading of the xml line.
	  catch (string errorStr)
	  {
	    cout << "ERROR : " << errorStr << endl;
	  }
      }
 
      xmlfile.close();
  }
  else
  {
      cout << "Unable to open the file" << endl;
  }
}
 
/* print Out the outer XML values */
void xmlReader::printOuterXML()
{
    cout << "XML Reader Main Object (Xml main details) " << endl;
    for (int i =0; i < _xmlMainDetails.size(); i++)
      _xmlMainDetails.at(i).printOutXmlObject();
 
    cout << "XML Reader xml details" << endl;
    for (int i =0; i < _xmlDetails.size(); i++)
      _xmlDetails.at(i).printOutXmlObject();
}

Xml reader – cpp

The xml reader from the xmlreader in cpp joins together the xmlattribute and xmlobject and this last class, this one is the biggest because it does most of the work. So shall try and explain each part.

As before a xml is

<tagname attributesname="attributesvalue">value</tagname>

The main structure includes the xml definition ( ) in the _xmlMainDetails and the rest of the xml file is in _xmlDetails vectors of xmlObject.

The main file is loaded with the loadLoad ( filename ) and then printout the xml that has been loaded, shall have to do some more public functions to view etc the loaded xml file, but this is just the basics of a xml reader. The rest are private functions because they will load the xml file in the _xmlDetails variable.

class xmlReader {
  protected:
    vector<xmlObject> _xmlMainDetails;
    vector<xmlObject> _xmlDetails;
 
  public: 
    xmlReader();
 
    // open file and read in the xml file and place into the _xmlDetails
    bool loadFile(string filename);
    void printOuterXML();
 
  private :
    xmlObject readLine(string xmlToSplitUp, string* tagName);
    string readUntilCharacter(string line, char characterStart, char characterEnd, string *returnLine);
    xmlAttribute getAttribute(string attributeString);
    vector<xmlAttribute> getAttributesFromString(string str);
};

The BADATTRIBUTE are const string values that I have included at the bottom, but they are within a try {} catch {} code, so that it will throw a error to be catch.

The getAttribute, will try and obtain the attribute from a string parameter, e.g. if the string parameter is in the style of attributename=”attributevalue”, then it will store the name and value in the xmlAttribute to return back to the calling method.

/*xmlReader*/
xmlReader::xmlReader()
{
}
 
// attribute is normally in the format of attributeName="attributeValue"
xmlAttribute xmlReader::getAttribute(string attributeString)
{
  xmlAttribute returnAttribute;
 
  // make sure that there is a = in the attribute string
  int findEqual = attributeString.find('=');
  if (findEqual > 0)
  {
    // set the attribute name to the substring till the equal
    returnAttribute._attributeName = attributeString.substr(0,findEqual);
    // make sure that there is some characters after the '=' sign.
    if (attributeString.length() > (findEqual+3))
    {
      returnAttribute._attributeValue = attributeString.substr(findEqual+2,(attributeString.length() - (findEqual +3)));
    }
    else
      throw BADATTRIBUTE;
  }else
    // if there does not appear to be ="" at the end of the string then throw a error.
    throw BADATTRIBUTE;
 
  return returnAttribute;
}

The getAttributesFromString if there are 0-x amount of attributes within a string it will process each one in turn and request the above method getAttributes to actually fill in the xmlAttribute variable.

vector<xmlAttribute> xmlReader::getAttributesFromString(string str)
{
    vector<xmlAttribute> returnAtt;
    xmlAttribute attribute;
    int args;
    char st1[CHARACTERLENGHT];
 
    // args normally equals 1 because there is a attribute present
    // else there was no attribute there, just do one at a time
    args = sscanf(str.c_str(), "%s", st1);
    while (args  == 1 && (str.length() > 1)) {
      // see if there is a real attribute attributeName="attributeValue"
      try {
	attribute = getAttribute(st1);
	// push back in the vector array the attribute
	returnAtt.push_back(attribute);
      } catch (string errorStr)		// any errors
      {
	cout << "ERROR : " << errorStr << endl;
      }
      // re-do the string to pull out any more attributes.
      str = str.substr(strlen(st1));
      // see if there is any more attributes present.
      args = sscanf(str.c_str(), "%s", st1);
    }
    return returnAtt;
}

readLine, is the main part of the xml reading object. Here is the basics of what this class is doing.

If there is no tagname within a xml line, return.

If the xml line is the main definition process that style of xml line input, and pull out any attributes that are present.

Else process a normal xml line input with attributes (if present) get the value of the tag and then check to make sure that the end tag name is the same as the first tagname.

// scan through the xml string and pull out the tags and the attributes and value.
xmlObject xmlReader::readLine(string xmlToSplitUp, string* tagName)
{
  xmlObject returnObj;
  string returnLine, value, endTagName;
  int findXml;
 
  // pick out the tag name, if none then return and throw a bad tag name error.
  *tagName = readUntilCharacter(xmlToSplitUp, '<','>', &returnLine);
  if (tagName->length() ==0)
  {
     throw BADTAGNAME;
     return returnObj;
  }
 
  // if there is a xml version etc in the tagname then process the xml version encoding values.
  findXml=tagName->find("xml");
  if ((findXml > 0 && findXml < tagName->length()) && tagName->length() > 1 )
  {
    // this is the xml version etc.
    // there should be ? at each end of the xml version statement
    string xmlStr = readUntilCharacter(*tagName, '?','?', &returnLine);
    if (returnLine != "?") 
    {
      throw BADXMLVERSION;
      return returnObj;
    }
    // go passed the xml characters.
    returnLine = xmlStr.substr(findXml+3);
    // read any of the attributes from the string
    returnObj.setAttributeVector(getAttributesFromString(returnLine));
    // I am storing the version and any other xml details, so set the return value to store in the correct place.
    returnObj.setXmlMainDetails(true);
  }else if (tagName->length() > 1) 
  {
    // need to see if there is any attributes
    int findTagAtts = tagName->find(' ');
    if (findTagAtts < tagName->length())
    {
      // the attributes are passed the space character in the tagName variable
      string attributes = tagName->substr(findTagAtts);
      // store only the tagName in the tagName variable since pulled out the attributes
      *tagName = tagName->substr(0,findTagAtts);
      // get the attributes into a vector and store in the return object
      returnObj.setAttributeVector(getAttributesFromString(attributes));
    }
 
    if (returnLine.length() > 1)
    {
      // pull out the value in the xml line <tagname>VALUE</tagname>
      value = readUntilCharacter(returnLine,'>','<',&returnLine);
      returnObj.setTagValue(value);
    }
    if (returnLine.length() > 1)
    {
      // pick out the end tag name and make sure it is the same as the first one.
      endTagName = readUntilCharacter(returnLine,'<','>',&returnLine);
      string compareEndTag = "/"+*tagName;
      //if the end tag is not the same as the tag name then throw a error.
      if (endTagName != compareEndTag) 
      {
	throw BADXMLTAGEND;
      } 
    }
    returnObj.setTagName(*tagName);
 
   }
  return returnObj;
}

readUntilCharacter, is to pick out a string between two character points e.g. string = “a place to run to, here is it, codingfriends.com” and you want to pick out “here is it” there is two ‘,’ each side of it. So to pull out the readUntilCharacter(“a place to run to, here is it, codingfriends.com”, ‘,’ , ‘,’ , &returnLine) the returnLine is the rest of the line after the last character searched for.

// pick out the characters between two character points, and also return the rest of the line.
string xmlReader::readUntilCharacter(string line, char characterStart, char characterEnd, string *returnLine)
{
  string returnString;
  // find the first occurrence of the character integer placement
  int firstChar = line.find(characterStart);
  // if there is one.
  if (firstChar >= 0)
  {
    // setup the return string, even if a second part cannot be found.
    returnString = line.substr(firstChar+1, (line.length()- firstChar)-1);
    int secChar = returnString.find(characterEnd);
    //if the secound part can be found
    if (secChar > 0)
    {
      *returnLine = returnString.substr(secChar, (returnString.length() - secChar));
      returnString = returnString.substr(0,secChar);
    }
  }
  return returnString;
}

loadFile will load the xml file into the private _xmlMainDetails and _xmlDetails and just see’s if file is present and try’s to load it.

// read in the XML file and place each line into the vector xmlObject 
bool xmlReader::loadFile(string filename)
{
  xmlObject xmlObj;
  string line, tagName;
 
  ifstream xmlfile(filename.c_str());
  if (xmlfile.is_open())
  {
      // if the xml version and also the encodingvalues are present.    
      while (!xmlfile.eof())
      {
	  // pull out the start tag and compare against the endtag
	  getline(xmlfile,line);
	  try 
	  {
	    // pick out the xml details from line and return a xmlObject 
	    // to add to the vector array of xml objects
	    // also return the tagName if any futher processing is required.
	    xmlObj = readLine(line, &tagName);
	    // if there is ?xml version etc details present store, else store into the main xml details
	    if (xmlObj.getXmlMainDetails())
	    {
	      _xmlMainDetails.push_back(xmlObj);
	    }
	    else
	    {  
	      _xmlDetails.push_back(xmlObj);
	    }
	  }
	  // if any error occur during the reading of the xml line.
	  catch (string errorStr)
	  {
	    cout << "ERROR : " << errorStr << endl;
	  }
      }
 
      xmlfile.close();
  }
  else
  {
      cout << "Unable to open the file" << endl;
  }
}

Will print out the xml loaded from the xml file.

/* print Out the outer XML values */
void xmlReader::printOuterXML()
{
    cout << "XML Reader Main Object (Xml main details) " << endl;
    for (int i =0; i < _xmlMainDetails.size(); i++)
      _xmlMainDetails.at(i).printOutXmlObject();
 
    cout << "XML Reader xml details" << endl;
    for (int i =0; i < _xmlDetails.size(); i++)
      _xmlDetails.at(i).printOutXmlObject();
}

here are the constant string values.

const int CHARACTERLENGHT = 80;
const string BADXMLVERSION = "Xml version - first line ? - problem";
const string BADTAGNAME = "Tag name was not present";
const string BADXMLTAGEND = "End tag is not the same as tag name";
const string BADATTRIBUTE = "Attribute in wrong format attributeName=\"attributeValue\"";

Xml object – c++

From the main project xmlreader, here is the xml object that I created to have a xml line of code which holds the details of a basic xml.

The main basics of a xml are as below

<tagname attributesname="attributesvalue">value</tagname>

So I will need to store the tagname, attributes and the value. Here is the class structure that I did come up with.

class xmlObject {
  private :
    string _tagName, _tagValue;
    vector<xmlAttribute> _attributes;
    bool _xmlMainDetails;
 
  public:
      xmlObject() { _xmlMainDetails = false;} ;
      xmlObject(string tag, string tValue, xmlAttribute attribute);
 
      void setTagName(string tagName);
      void setTagValue(string tagValue);
      void addAttributes(xmlAttribute attribute);
      void setAttributeVector(vector<xmlAttribute> setAtt);
      void setXmlMainDetails(bool value);
      bool getXmlMainDetails();
      void printOutXmlObject();
};

The set/getXmlMainDetails are if the are at the top of the xml file and need to store them in a different place.

A vector is a nice array basically, it allows to dynamically increment the size of the array with using the push_back (and the opposite to shrink pop_back).

The basics of a vector are as below, means to have a vector of type int

vector<int> intvector;

Here is the class implementation of the object structure xmlObject

/* xmlObject */
// constructor for xmlObject, if any details are passed whilst constructing 
xmlObject::xmlObject(string tag, string tValue, xmlAttribute attribute)
{
  _tagName = tag;
  _tagValue = tValue;
  _attributes.push_back(attribute);
}
 
// xml <tagname attributes="attributesvalue">VALUE</tagname>
// set the tag name
void xmlObject::setTagName(string tagName)
{
  _tagName = tagName;
}
 
// set tag value
void xmlObject::setTagValue(string tagValue)
{
  _tagValue = tagValue;
}
 
// add attributes to the vector attributes variable
void xmlObject::addAttributes(xmlAttribute attribute)
{
  _attributes.push_back(attribute);
}
 
// fill in the vector attributes variable.
void xmlObject::setAttributeVector(vector<xmlAttribute> setAtt)
{
  _attributes = setAtt;
}
 
// print out the xml object detais, with the attributes values.
void xmlObject::printOutXmlObject()
{
  cout << "XML Object" << endl;
  cout << "Tagname  :" << _tagName << endl;
  cout << "Tagvalue :" << _tagValue << endl;
  for (int i= 0; i < (int)_attributes.size(); i++)
  {
      cout << "Attribute " << i << " : Name : "<< _attributes.at(i)._attributeName << " Value : " << _attributes.at(i)._attributeValue << endl;
  }
}
 
// set the main set details value
void xmlObject::setXmlMainDetails(bool value)
{	
    _xmlMainDetails = value;
}
 
// get a boolean value to see if the xmlObject is the main <?xml .. ?> value
bool xmlObject::getXmlMainDetails()
{
  return _xmlMainDetails;
}

I shall post on how to implement/compile etc a class in two different files later on, in a lessons basics for different languages but on the whole, if you store the top structure in a .h header file and then the implementation in a .cpp file. Of course shall post the whole code to store in .h .cpp files accordlying for the whole project but this is just a stripped down version.