15. Exercise [Tax Calculation] with XML
In this exercise, which we have already covered many times, the server returns the results to the client in the form of an XML stream:
- <response><error>msg</error></response> in case of an error;
- <response><tax>value</tax></response> if the tax could be calculated.
We use what we have just learned about parsing an XML document.
![]() |
15.1. The Web Service
The web service is no different from the one studied previously, except that the XML response sent to the client is slightly different. The architecture remains the same:
![]() |
#!D:\Programs\ActivePython\Python2.7.2\python.exe
# -*- coding=Utf-8 -*-
# Import the Impots* class module
from impots import *
import cgi, cgitb, re
# Enable display of debug information
cgitb.enable()
# ------------------------------------------------
# the tax web service
# ------------------------------------------------
# The data required to calculate the tax has been placed in the MySQL table TABLE
# belonging to the BASE database. The table has the following structure
# limits decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# The parameters for taxable individuals (marital status, number of children, annual salary)
# are sent by the client in the form params=marital status, number of children, annual salary
# the results (marital status, number of children, annual salary, tax due) are returned to the client
# in the form <tax>value</tax>
# or in the form <error>msg</error>, if the parameters are invalid
# the server returns unformatted text to the client
print "Content-Type: text/plain\n"
# start of response
print "<response>"
# definition of constants
USER="root"
PWD=""
HOST="localhost"
DATABASE="dbimpots"
TABLE="taxes"
# instantiate [business] layer
try:
business=TaxBusiness(TaxMySQL(HOST,USER,PWD,DATABASE,TABLE))
except (IOError, TaxError) as info:
print("<error>An error occurred: {0}</error>".format(infos))
sys.exit()
# retrieve the line sent by the client to the server
params = cgi.FieldStorage().getlist('params')
# if no parameters, then error
if not params:
print "<error>The parameter [params] is missing<error></response>"
sys.exit()
# process the params parameter
#print "Parameters received --> %s\n" %(params)
items = params[0].strip().lower().split(',')
# there must be exactly 3 fields
if len(items) != 3:
print "<error>[%s]: invalid number of parameters</error></response>" % (params[0])
sys.exit()
# the first parameter (marital status) must be yes/no
marie = items[0].strip()
if married != "yes" and married != "no":
print "<error>[%s]: Invalid first parameter</error></response>\n" % (params[0])
sys.exit()
# The second parameter (number of children) must be an integer
match = re.match(r"^\s*(\d+)\s*$", items[1])
if not match:
print "<error>[%s]: Invalid second parameter</error></response>\n" % (params[0])
sys.exit()
children = int(match.groups()[0])
# the third parameter (salary) must be an integer
match = re.match(r"^\s*(\d+)\s*$", items[2])
if not match:
print "<error>[%s]: Invalid second parameter</error></response>\n" % (params[0])
sys.exit()
salary = int(match.groups()[0])
# calculate the tax
tax = job.calculate(spouse, children, salary)
# return the result
print "<tax>%s</tax></response>\n" % (tax)
# end
Notes:
This web service differs from the previous one only in the nature of its response:
<response><error>msg</error></response> in case of an error instead of <error>msg</error>
<response><tax>value</tax></response> if the tax could be calculated instead of <tax>value</tax>
15.2. The client application
Our client must parse the XML response sent by the web service. We apply what we learned in the analysis of an XML document.
# -*- coding=utf-8 -*-
import httplib, urllib, re
import xml.sax, xml.sax.handler
# XML handling class
class XmlHandler(xml.sax.handler.ContentHandler):
# function called when an opening tag is encountered
def startElement(self, name, attributes):
# note the current element
global currentElement
currentElement = name.strip().lower()
# Function called when an end tag is encountered
def endElement(self, name):
# do nothing
pass
# the data handling function
def characters(self, data):
# data
global current_element, elements
# the data is retrieved
match = re.match(r"^\s*(.+?)\s*$", data)
if match:
elements[currentElement] = match.groups()[0].lower()
def getXmlResults(response):
# parse the XML response
xml.sax.parseString(response, XmlHandler())
# return the results
if elements.has_key('error'):
return (elements['error'], "")
else:
return ("", elements['tax'])
# ------------------------------------------------------------ main
# constants
HOST="localhost"
URL="/cgi-bin/impots_web_02b.py"
data=("yes,2,200000","no,2,200000","yes,3,200000","no,3,200000","x,y,z,t","x,2,200000", "yes,x,200000","yes,2,x");
# global variables
current_element=""
elements={}
# connection
connection = httplib.HTTPConnection(HOST)
# logging
#connection.set_debuglevel(1)
# loop through the data to send to the server
for params in data:
# parameters must be encoded before being sent to the server
params = urllib.urlencode({'params': params})
# send the request
connection.request("POST", URL, parameters)
# process the response (XML stream)
response = connection.getresponse().read()
# parse the XML file
(error, result) = getXmlResults(response)
if not error:
print "impot[%s]=%s" % (params, impot)
else:
print "error[%s]=%s" % (params, error)
Notes:
- The XML feed from the web service is processed by the getResultatsXml function (line 60);
- line 29: the getResultatsXml function;
- line 31: the web service's XML response is parsed by an instance of the XmlHandler class defined on line 6;
- line 6: the XmlHandler class implements the three methods startElement, endElement, and characters. Using these three methods, a dictionary is created. The keys are the names of the <error> and <import> tags, and the values are the data associated with these two tags;
- lines 33–36: the getResultatsXml function returns a tuple with two elements:
- (error, "") if the XML stream analysis detected the <error> tag. error then represents the content of this tag;
- ("", impot) if the XML stream analysis revealed the presence of the <impot> tag. impot then represents the content of this tag.
- Line 60: The result of the getResultatsXml function is retrieved and then processed in lines 61–64.

