Im Agent "poxServer" der
unten angehängten Datenbank ist Punkt "4. Server parst das xml in eine Datenstruktur" mit SAX Parsing implementiert. Als LotusScript. Die SAX-API eignet sich sehr gut, um xml zu lesen. Man kann damit
nicht xml schreiben oder editieren. Die SAX-API wurde in sehr vielen Programmiersprachen implementiert. Wenn man SAX-Parsing in LotusScript kann, ist es ein kleiner Schritt das auch in C#, Java, C++, Ruby oder was auch immer zu beherrschen.
SAX ist eine eventgesteuerte API. Man kann sich das so ähnlich vorstellen wie Lotus Notes ein Dokument verarbeitet. Öffnest du ein xml Dokument im Notes Client, dann werden:
Eine Menge Maskenevents aufgerufen (QueryOpen, PostOpen) und die Formeln der einzelnen Felder von links oben nach rechts unten werden berechnet.
So ähnlich verhält es sich mit dem
Handler eines xml Parsers.
Es gibt verschiedene Events, die man anmelden kann. Zum Beispiel:
On Event SAX_StartDocument from saxParser Call SAXStartDocument
On Event SAX_StartElement From saxParser Call SAXStartElement
On Event SAX_Characters From saxParser Call SAXCharacters
On Event SAX_EndElement From saxParser Call SAXEndElement
Die komplette Liste findet ihr irgendwo in der Notes Designer Dokumentation.
Im Parsingprozess werden dann die angemeldeten Unterroutinen aufgerufen.
(z.B. Sub SAXEndElement (Source As Notessaxparser, Byval ElementName As String))
Also das ist ein xml Dokument:
<?xml version="1" encoding="UTF-8"?>
<math-in>
<number parentesis="left">8</number>
<operator>+</operator>
<number parentesis="right">2</number>
<operator>*</operator>
<number>4</number>
</math-in>
Der SAX Parser geht das von oben bis unten durch und ruft bei definierten "Stellen" angemeldete Subroutinen auf.
Er macht das automatisch. Wir müssen nur auf ihn in den angemeldeten Subroutinen reagieren.
Mit den oben Beispielhaft angegebenen Events, sieht das so aus:
1. Parsing beginnt-> Ruft Call SAXStartDocument auf.
2. kommt an den öffnenden Tag <math> -> Ruft SAXStartElement auf
3. Kommt an den öffnenden Tag <number> -> Ruft SAXStartElement auf
4. Findet Text zwischen den öffnenden und schliessenden Tags <number> und </number>-> Ruft SAXCharaters auf.
5. Findet den schliessenden Tag </number> -> Ruft SAXSEndElement auf
3. Kommt an den öffnenden Tag <operator> -> Ruft SAXStartElement auf
4. Findet Text zwischen den öffnenden und schliessenden Tags <operator> und </operator>-> Ruft SAXCharaters auf.
5. Findet den schliessenden Tag </operator> -> Ruft SAXSEndElement auf
Und so weiter. Bis zum letzten schliessenden Tag </math>
Nun gibt es z.B. in den <number> tags Attribute. Was ist damit?
<number parentesis="left">
Nun die einzelnen angemeldeten Event-Subroutinen besitzen definierte Parameter.
Zum Beispiel:
Sub SAXStartElement (Source As Notessaxparser,_
Byval elementname As String, Attributes As NotesSaxAttributeList)
...
End Sub
In jedem Subroutinenaufruf von SAXStartElement wird
a) der Name des Tags als String übergeben (bei <math> wäre das math.
b) die Attribute des Tags als NotesSaxAttributeList übergeben, wobei NotesSaxAttributeList ein einfaches LotusScript Objekt ist. In der angehängten Datenbank könnt ihr euch anschauen wie das genauer verarbeitet wird.
Es ist zugegeben ein bischen gewöhnungsbedürftig, aber zum Lesen von xml Dokumenten ist die SAX Api oft einfacher als die DOM-Api oder traditionelles String-Parsing. Und zwar bedeutend.
Ich beschreibe jetzt noch ein wenig konkrete Implementierung des Agenten poxParser in der angehängten Datenbank:
Der Agent holt sich von der Funktion getStrMockIn() as String ein XML-Dokument als String zum Testen. Dieser String wird in einen NotesStream geladen (sehr einfach)
Set stream = session.CreateStream
stream.WriteText(strIn)
Dann wird die Funktion
Function xmlProcessIn (streamIn As NotesStream) As Variant
aufgerufen, die das SAXParsing steuert.
Das eigentlich SAXParsing findet dann in
Class MySaxContentHandler
in den Declarations statt.
Ein Objekt dieser Klasse wird von xmlProcessIn erzeugt. Der Konstruktor
Sub new (streamIn As NotesStream)
wird aufgerufen. Dort wird ein saxParser as NotesSAXParser Objekt erzeugt, die events werden angemeldet.
Sub new (streamIn As NotesStream)
' the data to be retrieved later is saved here
Redim Preserve mathElem(0)
Set sAXParser = session.CreateSAXParser(streamIn)
' the events to be observed during parsing. calls the subroutines below.
On Event SAX_StartElement From saxParser Call SAXStartElement
On Event SAX_Characters From saxParser Call SAXCharacters
On Event SAX_EndElement From saxParser Call SAXEndElement
On Event SAX_Error From saxParser Call SAXError
On Event SAX_FatalError From saxParser Call SAXFatalError
On Event SAX_Warning From saxParser Call SAXWarning
End Sub
von xmlProcessIn wird dann als nächstes der ParsingProzess gestartet.
xmlProcessIn:
Set instMySaxContentHandler = New MySaxContentHandler(streamIn)
instMySaxContentHandler.process
In Class MySaxContentHandler:
Function process () As Variant
saxParser.process
End Function
Die Kontrolle geht nun auf den SAXParser über. Er ruft nun die angemeldeten Event-Funktionen Call SAXStartElement, Call SAXCharacters, etc auf, sobald er während des Parsens von oben nach unten auf ein entsprechendes "Event" "stösst" (z.B. öffnender Tag, Text zwischen öffnenden und schliessenden Tags, schliessender Tag, etc).
In den aufgerufenen Subroutinen wird nun ein Array des ebenfalls in den Declaration definierten Types Math Element erzeugt
Type MathElement
name As String
attribute As String
value As String
End Type
In der Instanz der Klasse MySaxContentHandler ist dieser Array eine Public Member-Variable:
Class MySaxContentHandler
Public mathElem() As MathElement
[...]
Hat der SAXParser sämtliche Elemente von oben nach unten durchgearbeitet fällt die Programmkontrolle wieder zurück auf die oben erwähnte Function xmlProcessIn zurück, die den Parsing Prozess angestossen hat. Diese Funktion gibt dann noch die Werte des Arrays mathElem aus.
For i= 1 To Ubound(instMySaxContentHandler.mathElem())
currentMathElement = instMySaxContentHandler.mathElem(i)
'Msgbox instMySaxContentHandler.arrValues(i), MB_ICONINFORMATION, instMySaxContentHandler.arrKeys(i)
strDebug = strDebug & Chr$(13) & Chr$(10) & |MathElement(| & Cstr(i) & |)-->name="| & currentMathElement.name &_
|" value="| & currentMathElement.value & |" attribute="| & currentMathElement.attribute & |"|
'& |"|
Next
Msgbox strDebug, MB_ICONINFORMATION, "result"
Interessierte sollten einfach den Agenten mal ausprobieren.
Das war Schritt 4:
4. Server parst das xml in eine Datenstruktur
Input: xml Dokument als String
Output: Datenstruktur in LotusScript (hier wirds erstmal ein Array eines Types sein)
Nicht für jeden der angesprochenen Schritte muss so viel Code geschrieben werden.
Gruß Axel