Skip to content

15. Übung [Steuerberechnung] mit XML

In dieser Übung, die wir bereits mehrfach behandelt haben, sendet der Server die Ergebnisse in Form eines XML-Streams an den Client zurück:

  • <response><error>msg</error></response> im Falle eines Fehlers;
  • <response><tax>value</tax></response>, wenn die Steuer berechnet werden konnte.

Wir wenden das gerade Gelernte zum Parsen eines XML-Dokuments an.

15.1. Der Webdienst

Der Webdienst unterscheidet sich nicht von dem zuvor untersuchten, außer dass die an den Client gesendete XML-Antwort leicht abweicht. Die Architektur bleibt unverändert:


Der Webdienst (impots_web_02)

#   !D:\Programs\ActivePython\Python2.7.2\python.exe

#    -*- coding=Utf-8 -*-
#    import of Impots* class module
from impots import *
import cgi,cgitb,re

#    allow debugging information to be displayed
cgitb.enable()

# ------------------------------------------------ 
#  tax web service
# ------------------------------------------------ 

#  the data required to calculate the tax has been placed in table mysqL TABLE
#  belonging to the BASE database. The table has the following structure
#    limits decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
#  taxable person parameters (marital status, number of children, annual salary)
#  are sent by the customer in the form params=marital status, number of children, annual salary
#  results (marital status, number of children, annual salary, tax payable) are returned to the customer
#  in the form <impot>value</impot>
#  or as <error>msg</error>, if parameters are invalid

#  the server returns unformatted text to the client
print "Content-Type: text/plain\n"
#    start of answer
print "<reponse>"

#    definition of constants
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

#    instantiation layer [metier]
try:
    metier=ImpotsMetier(ImpotsMySQL(HOTE,USER,PWD,BASE,TABLE))
except (IOError, ImpotsError) as infos:
    print ("<erreur>Une erreur s'est produite : {0}</erreur>".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 "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

#  we use the params parameter  
#   print "parameters received --> %s\n" %(params)
items=params[0].strip().lower().split(',')
#  there must be only 3 fields
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
#  first parameter (marital status) must be yes/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\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 "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
enfants=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 "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
salaire=int(match.groups()[0])
#  tax calculation
impot=metier.calculer(marie,enfants,salaire)
#  return the result
print "<impot>%s</impot></reponse>\n" % (impot)
#    end

Anmerkungen:

Dieser Webdienst unterscheidet sich vom vorherigen lediglich in der Art seiner Antwort:

<response><error>msg</error></response> im Falle eines Fehlers anstelle von <error>msg</error>

<response><tax>value</tax></response>, wenn die Steuer berechnet werden konnte, anstelle von <tax>value</tax>

15.2. Die Client-Anwendung

Unser Client muss die vom Webdienst gesendete XML-Antwort analysieren. Wir wenden dabei das an, was wir bei der Analyse eines XML-Dokuments gelernt haben.


Das Programm (client_impots_web_02)

#    -*- coding=utf-8 -*-

import httplib,urllib,re
import xml.sax, xml.sax.handler
#    management class XML
class XmlHandler(xml.sax.handler.ContentHandler):

    #  function called when a start tag is encountered
    def startElement(self,name,attributs):
        #  note the current element
        global elementcourant
        elementcourant=name.strip().lower()

    #  the function called when an end tag is encountered
    def endElement(self,name):
        #    we do nothing
        pass

    #  the data management function
    def characters(self,data):
        #    data
        global elementcourant,elements

        #  data are retrieved
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()

def getResultatsXml(reponse):
    #  we analyze the XML response
    xml.sax.parseString(reponse,XmlHandler())
    #  we return the results
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

#    ------------------------------------------------------------ main
#    constant
HOST="localhost"
URL="/cgi-bin/impots_web_02b.py"
data=("oui,2,200000","non,2,200000","oui,3,200000","non,3,200000","x,y,z,t","x,2,200000", "oui,x,200000","oui,2,x");
#    global variables
elementcourant=""
elements={}

#    connection
connexion=httplib.HTTPConnection(HOST)
#    follow-up
#   connexion.set_debuglevel(1)
#  loop over the data to be sent to the server
for params in data:
    #  parameters must be encoded before being sent to the server
    parametres = urllib.urlencode({'params': params})
    #  send request
    connexion.request("POST",URL,parametres)
    #    response processing (stream XML)
    reponse=connexion.getresponse().read()
    #    xml file processing
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Hinweise:

  • Der XML-Feed vom Webdienst wird von der Funktion getResultatsXml verarbeitet (Zeile 60);
  • Zeile 29: die Funktion „getResultatsXml“;
  • Zeile 31: Die XML-Antwort des Webdienstes wird von einer Instanz der in Zeile 6 definierten Klasse „XmlHandler“ geparst;
  • Zeile 6: Die Klasse XmlHandler implementiert die drei Methoden startElement, endElement und characters. Mit diesen drei Methoden wird ein Wörterbuch erstellt. Die Schlüssel sind die Namen der Tags <error> und <import>, und die Werte sind die mit diesen beiden Tags verbundenen Daten;
  • Zeilen 33–36: Die Funktion getResultatsXml gibt ein Tupel mit zwei Elementen zurück:
    • (error, "") wenn die XML-Stream-Analyse das <error>-Tag erkannt hat. error repräsentiert dann den Inhalt dieses Tags;
    • ("", impot), wenn die XML-Stream-Analyse das Vorhandensein des <import>-Tags festgestellt hat. impot repräsentiert dann den Inhalt dieses Tags.
  • Zeile 60: Das Ergebnis der Funktion getResultatsXml wird abgerufen und anschließend in den Zeilen 61–64 verarbeitet.

15.3. Die Ergebnisse

1
2
3
4
5
6
7
8
impot[oui,2,200000]=22504.0
impot[non,2,200000]=33388.0
impot[oui,3,200000]=16400.0
impot[non,3,200000]=22504.0
erreur[x,y,z,t]=[x,y,z,t] : nombre de parametres invalide
erreur[x,2,200000]=[x,2,200000] : 1er parametre invalide
erreur[oui,x,200000]=[oui,x,200000] : 2ieme parametre invalide
erreur[oui,2,x]=[oui,2,x] : 2ieme parametre invalide