Dieser Artikel beschäftigt sich mit der Anbindung von XML-Daten an die ISIS Papyrus DocEXEC-Verarbeitung. Der Artikel setzt voraus, dass der Leser oder die Leserin mit der Arbeit im ISIS Papyrus Designer vertraut ist.
ISIS Papyrus DocEXEC.
Die ISIS Papyrus DocEXEC läuft auf Windows, OS/2, Unix, Linux und AIX und erstellt dynamische Dokumente im AFP- oder PDF-Format. (AFP basiert auf dem von IBM definierten MO: DCA und ist ein hardwareunabhängiges Druckformat.)
Viele Arten von Eingabedatensätzen sind möglich: ASCII, ANSII oder EBCDIC, Daten in jeder beliebigen Codepage, Unicode oder nicht und mit oder ohne Channel Code.
Die Anbindung von XML empfiehlt ISIS nur dann, wenn eine andere Alternative nicht existiert oder das Druckaufkommen eher gering ist – XML trägt redundante Informationen mit sich; bei großem Druckaufkommen zieht dies unter Umständen eine verringerte Performance nach sich.
Doch immer mehr Bestandssysteme liefern Daten im XML-Format. Immer öfter stellt sich also auch die Frage, ob ISIS Papyrus damit umgehen kann. Die kurze Antwort lautet: Ja, DOCDEF (der Code, in dem die Dokumente definiert werden), ermöglicht die Verarbeitung von XML-Eingabedaten und zwar mittels des Befehls XmlRead. XmlRead verwendet den SAX 2.0 – Standard (SAX =“Simple API“).
Die Methode der Anbindung unterscheidet sich allerdings von der in ISIS Papyrus sonst üblichen: ein generischer Parser muss vom Anwender selbst im DOCDEF–Code erstellt werden – die oben genannte Kommandos XmlRead und XmlSet bieten die Grundlage dazu.
Der XML-Parser: Die Befehle XmlRead und XmlSet.
Zunächst muss man wissen, wie die Befehle XmlRead und XmlSet funktionieren.
XmlRead
- Parst das XML und speichert die Bezeichnung jedes Tags in einer Variablen ([PRÄFIX_]NAME). Auch andere Informationen werden in Variablen abgelegt:
- PRÄFIX_CLOSE (XML Datei geschlossen)
- PRÄFIX_URI (URL)
- PRÄFIX _QNAME (Qualifizierter XML-Name)
- Außerdem erlaubt XmlRead es, auf jedes auftretende XML-Start-Tag mit dem Aufruf eines eigenen DOCFORMATS zu reagieren – ebenso wie auf jedes End Tag
XmlSet
- unterbricht den Parse-Prozess von XmlRead.
- speichert die Werte zwischen Start Tag und End Tag in eine Variable
- erlaubt den Aufruf eines DocFormats bei Erhalt eines Start- und bei Erhalt eines End-Tags
- Schließt die XML-Datei
XML – eine beispielhafte Anbindung
Im Folgenden soll die Implementation eines generischen Parsers skizziert werden. Dieser ist so konzeptioniert, dass er mit sehr tiefen Hierarchieebenen umgehen kann, ohne für jedes Tag ein eigenes DOCFORMAT zu benötigen.
Die Namen der aus der Tag-Hierarchie erzeugten Variablen spiegeln die Baumstruktur wieder: Great-grandparent_Grandparent_Parent_Child. Da die Handhabung so langer Variablennamen umständlich sein kann, arbeitet unser Beispiel mit Referenzvariablen, auf die mit geschweiften Klammern zugegriffen wird.
Beispiel: ChildX = ‚Great-grandparent_Grandparent_Parent_Child‘. Zugriff {ChildX} -> übersetzt sich dann als Zugriff auf den Inahalt der Variablen Great-grandparent_Grandparent_Parent_Child.
Schauen wir uns die Implementation einmal an:
$BEFOREFIRSTDOC
Wie üblich nutzten wir $BEFOREFIRSTDOC, um einige globale Variablen zu setzen.
Zunächst merken wir uns den Namen der XML-Eingabedatei:
- Variable
- &XMLDATEINAME = $INPUT
Dann legen wir die Bezeichnung des Dokumenten-Tags fest, das heißt desjenigen Tags im XML, das die Daten eines Dokuments beinhaltet:
- Variable
- &DOCUMENTTAG = [Doku Tag in XML] (in unserem Beispiel also ‚DOC‘)
$BEFOREDOC
XML enthält in der Regel Hierarchieebenen. Diese müssen wir abbilden. Ziel ist es, die Namen unserer aus den Tags abgeleiteten Variablen so zu bilden, dass sie den XML-Pfad des korrespondierenden Tags wiedergeben: Grandparent_Parent_Child
Das geschieht in diesem Beispiel mittels eines Arrays, der nach Einlesen der untersten Hierarchieebene eines Tags erstellt wird. Da $BEFOREDOC für jedes Dokument neu ausgeführt wird, initialisieren wir hier die Hilfsvariablen, um diese Ebenen für die TAGs eines Dokuments während des Lesevorgangs zu ermitteln:
- TAG_Zaehler = 0 (Zähler der TAG-Ebene für das TAG_ARRAY, Hierarchiezähler)
- ZAEHL_INDEX = 0 (für gleichnamige Tags derselben Ebene).
- ZAEHL_INDEX_FLAG = 0 (steuert das Anfügens des ZAEHL _INDEX)
- 1 = Füge den ZAEHL_INDEX an den TAG-Namen an
- 2 = Füge den ZAEHL_INDEX nicht an
Zu guter Letzt lesen wir die XML-Datei bis zum nächsten Dokumententag ein:
DocFORMAT PARSE_XML
Nun muss das eingelesene XML geparst werden. Am besten geschieht dies in einem eigenen Modul. In unserem Beispiel soll dieses Modul PARSE_XML heißen
Wir rufen XmlRead auf. XmlRead verlangt im Dialog die Eingabe folgender Informationen:
- XML Filename:
Hier tragen wir die Variable ein, der wir den Namen der Eingabedatei zugewiesen haben. In unserem Fall: &XMLDATEINAME - XML-Variable Präfix:
hier geben wir das Präfix an, das alle von XmlRead automatisch erzeugten Variablen erhalten sollen. In unserem Fall tragen wir ‚X_‘ ein. Die automatisch erzeugte Variable mit dem Namen des aktiven Tags wird also „X_NAME“ heißen. - Validate XML:
Sollten wir unser XML mit einer XSD validieren wollen, müssen wir hier die Checkbox „Yes“ aktivieren. In unserem Beispiel lassen wir sie aus. - Enable zOS Accelerator:
Sollten wir das durch zOS beschleunigte Einlesen von XML aktivieren wollen, müssen wir hier die Checkbox „Yes“ aktivieren. In unserem Beispiel lassen wir sie aus. - Docformat on Error:
Hier geben wir an welches Modul (DocFormat) aufgerufen werden soll, falls während des Parsens ein Fehler auftritt. In unserem Beispiel nennen wir das Fehlermodul: XML_FEHLER.
In unserem Beispiel begnügen wir uns mit der Ausgabe einer Message. Das Modul wird hier daher auch nicht weiter behandelt. - Docformat on Tagbegin:
Hier wird das Modul (DocFormat) benannt, dass für jedes neue Tag aufgerufen werden soll. Wenn Xmlread das erste Mal ausgeführt wird, ruft es das hier eingetragene DocFormat auf. Das geschieht auch die folgenden Male, falls der Wert nicht mit XmlSet überschrieben wird. Die Definition, die zu Beginn eines Tags gültig ist, bleibt gültig bis das End-Tag für dieses Tag erreicht wurde – auch falls sie dazwischen überschrieben wird. In unserem Beispiel tragen wir XMLTAG_BEGIN ein. - DocFormat on Tagend
Hier wird das Modul (DocFormat) benannt, das bei Erreichen des Endtags aufgerufen werden soll. Änderungen durch XmlSet wirken hier auf dieselbe Weise wie bei ‚Docformat on Tagbegin‘. In unserem Beispiel tragen wir XMLTAG_END ein.
XMLTAG_BEGIN
Unser Modul XMLTAG_BEGIN dient wie der Name bereits andeutet dazu, den Parse-Vorgang für ein neues Tag zu beginnen. Hier ermitteln wir die Namen der Variablen, die wir aus den Tags gewinnen wollen.
Wie gehen wie folgt vor:
Wir setzen das Flag: XMLTAG_TYP auf 1. Das bedeutet: die Bearbeitung eines Tags beginnt. Solange dieser Wert auf 1 steht, ist das Ende der Bearbeitung des Tags nicht erreicht.
Im DocFormat TAG_LIST_COUNT ermitteln wir die vorhandenen Tags (wird das Ende des Dokumenten-Tags erreicht, wird DOCTAGS_READY auf 1 gesetzt) und die Anzahl der gleichnamigen TAGs derselben Ebene. Das DocFormat TAG_LIST_COUNT wird später im Text genauer beschrieben.
Ist die Variable DOCTAGS_READY auf 1 haben wir alle TAGs des Dokuments ermittelt und können nun die Namen der Variablen bilden.
Ob es sich um eines von mehreren gleichnamigen Tags derselben Ebene handelt, ermitteln wir durch das das Flag ZAEHL_INDEX_FLAG, das wir im DocFormat TAG_LIST_COUNT gesetzt haben – in Verbindung mit der Zählvariablen ZAEHL_INDEX; diese enthält den Index des aktuellen Tags. ZAEHL_INDEX_FLAG == 2 bedeutet, dass es sich bekanntermaßen um ein mehrfach vorkommendes Flag handelt. Ist eines von beiden der Fall, konkatenieren wir den Indexzähler an den Namen des Tags, der von XMLRead in die automatisch erstellte Variable X_NAME geschrieben wurde und merken uns den neuen Tag-Namen im Array TAG_ARRAY. Anderenfalls merken wir uns im selben Array nur den Inhalt von X_NAME.
Im Anschluss ermitteln wir in einer For-Schleife über den Ebenenzähler TAG_ZEAEHL die Namen der Variablen, welche die Tags repräsentieren sollen.
Ist der Zählindex 1, heißt die Variable einfach wie das Tag. Wir lesen ihn aus unserem Array in die Variable XMLTAG_NAME, die wir zum Schluss in XMLSet verwenden werden, um die eigentliche Variable, die das Tag repräsentiert zu erstellen. Handelt es sich um ein Child – Tag konkatenieren wir die Parent-Child Struktur, indem wir alle Elemente des Arrays aneinanderhängen. In diesem Fall erstellen wir die Variable selbst:
{XMLTAG_NAME!‘_ZAEHL’} = TAG_ARRAY[I].
TAG_LIST_COUNT
Ist der Indexzähler für die Tags einer Ebene 0, prüfen wir, ob das DOCUMENT_TAG DOC erreicht ist. Falls ja, setzten wir das Flag DOCTAG_READY auf 1. Falls Nein, prüfen wir, ob wir uns in einem offenen Tag befinden (XMLTAG_TYP == 1). Ist das der Fall, ermitteln wir, anhand des Tagnamens in welcher Ebene wir uns befinden und setzen die Zähler für die Tags dieser Ebene. Diese Methode setzt voraus, dass uns die möglichen indizierten Tags namentlich bekannt sind.
Am Ende des Moduls prüfen wir, ob das aktuelle Tag geschlossen wurde (XMLTAG_TYP == 0) und auch das Ende des Dokumententags erreicht wurde (DOCTAGS_READY == 0). In diesem Fall setzen wir den Indexzähler zurück auf 0. XMLTAG_TYP ist 0, wenn TAG_LIST_COUNT aus dem Modul XMLTAG_END aufgerufen wurde.
XMLTAG_END
Durch das Setzen des Flags XMLTAG_TYPE = 0 zeigen wir an, dass die Bearbeitung des aktuell gelesenen Tags abgeschlossen ist.
Im Anschluss ermitteln wir, ob der Name des aktuellen Tags der des Dokumententags ist. Falls ja merken wir uns im Flag FORMAT_DOC = 1, dass die Daten eingelesen wurden und die Formatierung des Dokuments beginnen kann. Außerdem unterbrechen wir den XmlRead – Befehl mittels ‚Break XmlRead‘. Wurde das Ende der XML-Datei erreicht, schließen wir den XmlRead Befehl außerdem (Close XmlRead).
Ist der Name des aktuellen Tags nicht der des Dokumententags rufen wir nochmal das Modul Tag_List_Count auf und werten im Anschluss DOCTAGS_READY aus. Ist es wahr, verwenden wir XmlSet um aus dem Inhalt von XMLTAG_NAME eine Variable zu bilden.
Damit ist das Ende unseres Beispielmoduls PARSE_XML erreicht. Im Anschluss wird das Flag FORMAT_DOC geprüft. Steht es auf 1, können die Befehle zur Formatierung des Dokuments folgen.
Exkurs: Aliase für die Variablennamen
Zum Abschluss noch ein Tip für den Umgang mit den Variablennamen.
Wie oben erklärt spiegeln die Variablennamen die Tag–Hierarchie. Also:
Produktgruppe1_Produkt1_Produktdetails_Produktmasse_Breite
Produktgruppe1_Produkt2_Produktdetails_Produktmasse_Breite
Produktgruppe1_Produkt3_Produktdetails_Produktmasse_Breite
Produktgruppe2_Produkt1_Produktdetails_Produktmasse_Breite
Produktgruppe2_Produkt2_Produktdetails_Produktmasse_Breite
Produktgruppe2_Produkt3_Produktdetails_Produktmasse_Breite
Der Vorteil dieser Vorgehensweise ist, dass die Variablennamen eindeutig sind. Die Namen werden auf diese Weise unter Umständen aber sehr lang – je nach Tiefe der Hierarchie. Eine Möglichkeit, den Umgang mit sehr langen Variablennamen zu vereinfachen, ist es mit Aliasen zu arbeiten. Das würde in unserem Beispiel wie folgt funktionieren:
Der Zugriff auf die Werte der Variablen erfolgt dann ganz einfach so:
Schlussbemerkungen
Damit sind wir am Ende unsereres Beispiels für die Anbindung von Isis Papyrus DocEXEC an XML-Eingabedaten angelangt. Es sei erwähnt, dass dies nicht der einzige Weg ist, einen generischen XML-Parser zu schreiben. Beispielsweise wäre es auch möglich, für jedes Tag ein eigenes DocFormat zu verwenden. In diesem Fall müssten wir ‚Docformat on Tagbegin‘ und ‚Docformat on Tagend‘ für jedes neue Tag überschreiben – in XMLSet. Wirksam werden diese Änderungen dann immer, wenn ein neues Tag eingelesen wird. Der vorgestellte Weg hat jedoch den Vorzug mit je einem DocFormat für Tagbegin und Tagende auszukommen und die Hierarchie dennoch sauber abzubilden.
Die Verwendung von Referenzvariablen ermöglicht uns die einfache Handhabung langer Variablennamen, die unweigerlich entstehen, wenn man den XML-Pfad im Variablennamen abbildet. Alternativ könnte man natürlich auch darauf verzichten, den Pfad im Namen der Variablen zu spiegeln. Dies birgt jedoch die Gefahr, die Eindeutigkeit zu verlieren, falls Tagnamen mehrfach vorkeommen – sei es auf der selben Ebene oder in verschiedenen Haupttags.