Inhaltsverzeichnis


Zurück zur Übersichtsseite - TUSTEP und XML


Integration von XQuery in TUSTEP-Workflow

- chmoser chmoser

Ausgangslage

Beim Arbeiten mit Tustep an XML-Daten kann für spezifische Aufgaben schon einmal der Wunsch aufkommen, das Problem mit XQuery statt mit Tustep/Tuscript anzugehen. Gründe dafür könnten sein, dass sich die Aufgabe mit Tustep/Tuscript zwar lösen ließe, aber nur mit einem ungleich höheren Programmieraufwand im Vergleich zu XQuery. Weiter können - zum Beispiel im Umgang mit sehr großen Datenmengen - Performanzgründe für XQuery sprechen, das mit seinem funktionalen Zugang dem prozeduralen Zugang von Tuscript für gewisse Aufgabenstellungen (z.T. deutlich) überlegen ist.
Glücklicherweise offeriert Tustep die Möglichkeit, solche externen Prozesse aufzurufen, so dass XQuery-Operationen bequem in eine Tustep/Tuscript-Prozedur integriert und automatisiert aufgerufen werden können.

Lösung

Der Aufruf von solchen externen Prozessen geschieht mit folgender Anweisung:

$$ EXECUTE "pfad" "parameter"

Die entsprechende Beschreibung im Tustep-Handbuch:


Zu beachten ist also, dass die folgend beschriebenen Lösungen nur unter Windows Gültigkeit haben. (Außerdem wird eine Java-Installation vorausgesetzt).

Was genau rufen wir aber von Tustep aus auf? Wir rufen die Windows-Kommandozeile (cmd.exe) auf, die wiederum einen XQuery-Prozessor aufruft und diesem gewisse Parameter übergibt.
Als XQuery-Prozessor bietet sich Saxon an, der in einer freien Version (Saxon-HE home edition) zur Verfügung steht (Download hier). In der XML-Entwicklungsumgebung Oxygen ist die Saxon Enterprise Edition bereits enthalten (Datei: Oxygen-Programmordner\lib\saxon9ee.jar). Wenn man diese über die Kommandozeile außerhalb von Oxygen aufruft, stehen zwar die lizenzpflichtigen Programmteile nicht zur Verfügung, die Basis-Komponenten reichen aber für die meisten Zwecke problemlos aus.

Beim Aufruf von Saxon können dem Programm unzählige Parameter übergeben werden. Eine Übersicht finden sich hier. Im Folgenden beschränken wir uns aber auf die notwendigen Parameter. Die Syntax mit den minimalen Angaben lautet:

java -cp Pfad\zu\Saxon net.sf.saxon.Query -s:Pfad\zu\Eingabedatei -qs:XQuery-Statement -o:Pfad\zu\Ausgabedatei

Wir brauchen also 1) den Pfad zu Saxon, 2) den Pfad zur Eingabedatei, 3) die eigentliche XQuery-Abfrage und 4) den Pfad zur Ausgabedatei.

Beispiel:

java -cp "C:\Program Files\Oxygen XML Editor 15\lib\saxon9ee.jar" net.sf.saxon.Query -s:"C:\Users\CM\Documents\Tustep\beispiel\eingabe.xml" -qs:"//name" -o:"C:\Users\CM\Documents\Tustep\beispiel\ausgabe.xml"

Erläuterung:


Nun soll das Ganze nicht direkt über die Windows-Kommandozeile aufgerufen werden, sondern aus Tustep heraus, was dann folgendermaßen aussieht (der besseren Übersicht wegen werden die Pfadangaben einer Variablen zugewiesen, sie könnten aber auch direkt angegeben werden):

#makro
$$ MODE TUSCRIPT
...
SET pfadsaxon  = "C:\Program Files\Oxygen XML Editor 15\lib\saxon9ee.jar"
SET pfadinput  = "C:\Users\CM\Documents\Tustep\beispiel\eingabe.xml"
SET pfadoutput = "C:\Users\CM\Documents\Tustep\beispiel\ausgabe.xml"

EXECUTE "C:\Windows\System32\cmd.exe" *
DATA /C java -cp "{pfadsaxon}" net.sf.saxon.Query -s:{pfadinput}
DATA -qs://name
DATA -o:{pfadoutput}

...
*eof<code>
\\ **Variante 1: XQuery-Statement in eine Datei auslagern**\\  Statt über den Parameter "-qs" das XQuery-Statement direkt zu übergeben, kann mit dem Parameter "-q" eine Datei angegeben werden, die die entsprechende XQuery-Abfrage enthält:\\ 
<code>#makro
$$ MODE TUSCRIPT
...
...
EXECUTE "C:\Windows\System32\cmd.exe" *
DATA /C java -cp "{pfadsaxon}" net.sf.saxon.Query -s:{pfadinput}
DATA -q:C:\Users\CM\Documents\Tustep\beispiel\abfrage.xquery
DATA -o:{pfadoutput}

...
*eof


Variante 2: Ausgabe in Zwischenablage umleiten
Statt das Ergebnis in eine Datei auszugeben, kann es in die Zwischenablage kopiert werden. Dazu muss am Ende des Aufrufs statt der Ausgabedatei der Befehl | clip angegeben werden (dies ist eine Funktion der Windows-Kommandozeile, nicht des Saxon-Prozessors), also:

java -cp Pfad\zu\Saxon net.sf.saxon.Query -s:Pfad\zu\Eingabedatei -qs:XQuery-Statement | clip

Der Inhalt der Zwischenablage kann dann in Tustep mit

$$ FETCH/CLIPBOARD variablenname

in die Variable „variablenname“ übernommen und weiterverarbeitet werden.
Dies zumindest in der Theorie: bei meinen Versuchen wurde die Ausgabe zwar korrekt in die Zwischenablage geleitet, allerdings konnte der Zwischenablageinhalt sodann nicht in Tustep übernommen werden.


Beispiel 1

Nun soll das Ganze konkret an einem „realen“ Beispiel durchgespielt werden.
Die Eingabedaten bestehen aus Informationen zu verschiedenen Konzerten:

<?xml version="1.0" encoding="UTF-8"?>
<konzerte>
    <konzert id="868">
        <datum>1947-12-16</datum>
        <titel>10. Volkskonzert</titel>
        <leitung>Sturzenegger, Max</leitung>
        <ort>Berlin</ort>
        <werke>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>"Ohne Sonne", sechs Gesänge mit Orchester</werktitel>
                <solisten>
                    <solist>
                        <name>Ott, Mabella</name>
                        <instrument>Alt</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>Persischer Tanz aus der Oper "Chowantschina"</werktitel>
            </werk>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>"Lieder und Tänze des Todes" für Alt und Orchester</werktitel>
                <solisten>
                    <solist>
                        <name>Ott, Mabella</name>
                        <instrument>Alt</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Cajkovskij, Pëtr I.</komponist>
                <werktitel>Sinfonie Nr. 4, in f moll, op. 36</werktitel>
            </werk>
        </werke>
    </konzert>
    <konzert id="869">
        <datum>1947-12-18</datum>
        <titel>11. Volkskonzert</titel>
        <leitung>Sturzenegger, Max</leitung>
        <ort>Zürich</ort>
        <werke>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>"Ohne Sonne", sechs Gesänge mit Orchester</werktitel>
                <solisten>
                    <solist>
                        <name>Ott, Mabella</name>
                        <instrument>Alt</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>Persischer Tanz aus der Oper "Chowantschina"</werktitel>
            </werk>
            <werk>
                <komponist>Musorgskij, Modest P.</komponist>
                <werktitel>"Lieder und Tänze des Todes" für Alt und Orchester</werktitel>
                <solisten>
                    <solist>
                        <name>Ott, Mabella</name>
                        <instrument>Alt</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Cajkovskij, Pëtr I.</komponist>
                <werktitel>Sinfonie Nr. 4, in f moll, op. 36</werktitel>
            </werk>
        </werke>
    </konzert>
    <konzert id="882">
        <datum>1947-12-19</datum>
        <titel>4. Jugend-Konzert</titel>
        <leitung>Hartogs, Eduard</leitung>
        <ort>Heidelberg</ort>
        <werke>
            <werk>
                <komponist>Mozart, Wolfgang Amadeus</komponist>
                <werktitel>Ouvertüre zu "Der Schauspieldirektor" (K.V. 486)</werktitel>
            </werk>
            <werk>
                <komponist>Mendelssohn Bartholdy, Felix</komponist>
                <werktitel>Violinkonzert in e-moll, op. 64</werktitel>
                <solisten>
                    <solist>
                        <name>Jaques, Olivier</name>
                        <instrument>Violine</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Schubert, Franz</komponist>
                <werktitel>Sinfonie Nr. 5, in B-dur</werktitel>
            </werk>
        </werke>
    </konzert>
    <konzert id="2090">
        <datum>1947-12-26</datum>
        <titel>Nachweihnachts-Konzert</titel>
        <leitung>Schmid, Erich</leitung>
        <ort>Zürich</ort>
        <werke>
            <werk>
                <komponist>Labole, P. N.</komponist>
                <werktitel>"Le Flambojant", Marsch</werktitel>
            </werk>
            <werk>
                <komponist>Sibelius, Jean</komponist>
                <werktitel>"Finlandia", Tondichtung</werktitel>
            </werk>
            <werk>
                <komponist>Scarlatti, Domenico</komponist>
                <werktitel>Aus dem Konzert für Oboe und Klavier</werktitel>
                <solisten>
                    <solist>
                        <name>Blumer, Ernst</name>
                        <instrument>Oboe</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Borodin, Aleksandr P.</komponist>
                <werktitel>"Im Kloster"</werktitel>
            </werk>
            <werk>
                <komponist>Dvorák, Antonín</komponist>
                <werktitel>"Legende Nr. 4"</werktitel>
            </werk>
            <werk>
                <komponist>Bergson, Michael</komponist>
                <werktitel>"Im Norden, im Süden", Scene und Arie für Klarinette und Klavier</werktitel>
                <solisten>
                    <solist>
                        <name>Blumer, Fritz</name>
                        <instrument>Klarinette</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Saint-Saëns, Camille</komponist>
                <werktitel>Marche Héroique</werktitel>
            </werk>
            <werk>
                <komponist>Eilenberg, Richard</komponist>
                <werktitel>Maienfest-Walzer</werktitel>
            </werk>
            <werk>
                <komponist>Silva, Cayetano Alberto</komponist>
                <werktitel>"San Lorenzo", Marsch</werktitel>
            </werk>
        </werke>
    </konzert>
    <konzert id="870">
        <datum>1948-01-06</datum>
        <titel>12. Volkskonzert</titel>
        <leitung>Stoutz, Edmond de</leitung>
        <ort>Zürich</ort>
        <werke>
            <werk>
                <komponist>Torelli, Giuseppe</komponist>
                <werktitel>Concerto a due cori, in D-dur, Nr. 32</werktitel>
            </werk>
            <werk>
                <komponist>Purcell, Henry</komponist>
                <werktitel>Pavane und Chaconne, in g-moll, für Streichorchester</werktitel>
            </werk>
            <werk>
                <komponist>Stravinsky, Igor</komponist>
                <werktitel>Violinkonzert in D</werktitel>
                <solisten>
                    <solist>
                        <name>Baumgartner, Paul</name>
                        <instrument>Violine</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Mozart, Wolfgang Amadeus</komponist>
                <werktitel>Adagio in E-dur und Rondo in C-dur für Violine und Orchester</werktitel>
                <solisten>
                    <solist>
                        <name>Baumgartner, Paul</name>
                        <instrument>Violine</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Haydn, Joseph</komponist>
                <werktitel>Sinfonie in G-dur, Nr. 88</werktitel>
            </werk>
        </werke>
    </konzert>
    <konzert id="845">
        <datum>1948-01-13</datum>
        <titel>VI. Abonnementskonzert</titel>
        <leitung>Andreae, Volkmar</leitung>
        <ort>Basel</ort>
        <werke>
            <werk>
                <komponist>Cherubini, Luigi</komponist>
                <werktitel>Anakreon-Ouvertüre</werktitel>
            </werk>
            <werk>
                <komponist>Brun, Fritz</komponist>
                <werktitel>Klavierkonzert in A-dur (1946)</werktitel>
                <solisten>
                    <solist>
                        <name>Hirt, Franz Josef</name>
                        <instrument>Klavier</instrument>
                    </solist>
                </solisten>
            </werk>
            <werk>
                <komponist>Brahms, Johannes</komponist>
                <werktitel>Sinfonie Nr. 3, in F-dur, op. 90</werktitel>
            </werk>
        </werke>
    </konzert>
</konzerte>

Die Datei heißt „konzerte.xml“ und befindet sich in C:\Users\CM\Documents\Tustep\beispiel\konzerte.xml.
Das Ergebnis soll nach C:\Users\CM\Documents\Tustep\beispiel\konzerteneu.xml ausgegeben werden.
Für die Aufgabe, die es zu lösen gilt, verbleiben wir einstweilen beim Trivialen: es sollen (aus welchem Grund auch immer…) die Orte derjenigen Konzerte ausgegeben werden, in denen sowohl ein Werk von Mozart als auch von Schubert aufgeführt wurde.

Das XQuery-Statement, das das Gewünschte leistet, lautet:

for $n in //konzert
where $n//komponist = 'Mozart, Wolfgang Amadeus' and $n//komponist = 'Schubert, Franz'
return $n/ort

Aufgerufen aus Tustep heraus:

#makro
$$ MODE TUSCRIPT
SET pfadsaxon  = "C:\Program Files\Oxygen XML Editor 15\lib\saxon9ee.jar"
SET pfadinput  = "C:\Users\CM\Documents\Tustep\beispiel\konzerte.xml"
SET pfadoutput = "C:\Users\CM\Documents\Tustep\beispiel\konzerteneu.xml"

EXECUTE "C:\Windows\System32\cmd.exe" *
DATA /C java -cp "{pfadsaxon}" net.sf.saxon.Query -s:{pfadinput}
DATA -qs:"for $n in //konzert where $n//komponist = 'Mozart, Wolfgang Amadeus' and $n//komponist = 'Schubert, Franz' return $n/ort"
DATA -o:{pfadoutput}
*eof

Das Resultat in C:\Users\CM\Documents\Tustep\beispiel\konzerteneu.xml:

<?xml version="1.0" encoding="UTF-8"?>
<ort>Heidelberg</ort>



Beispiel 2

Nun ein etwas fortgeschritteneres Beispiel. Zusätzlich zur Datei konzerte.xml (siehe oben) steht nun eine Datei kritiken.xml zur Verfügung, die Daten zu Konzertkritiken enthält. Diese Daten bestehen aus den Namen der Verfasser von Konzertkritiken und deren Kritiken, die durch das ref-Attribut auf die Konzert-ids in der Datei konzerte.xml verweisen.
</code><?xml version=„1.0“ encoding=„UTF-8“?> <kritiken>

  <kritiker>
      <name>Müller, Max</name>
          <kritik ref="870">Nicht schlecht.</kritik>
          <kritik ref="869">Hab schon Besseres gehört.</kritik>
      <kritik ref="868">Nicht überzeugend.</kritik>
  </kritiker>
  <kritiker>
      <name>Meier, Fritz</name>
      <kritik ref="868">War super.</kritik>
      <kritik ref="845">Grottenschlecht.</kritik>
  </kritiker>
  <kritiker>
      <name>Fischer, Moritz</name>
      <kritik ref="845">Genial.</kritik>
      <kritik ref="2090">Enttäuschend.</kritik>
  </kritiker>
  <kritiker>
      <name>Bäcker, Barbara</name>
      <kritik ref="882">Schön.</kritik>
      <kritik ref="2090">Wunderbar.</kritik>
  </kritiker>

</kritiken></code> Die Aufgabe: Aus den beiden Dateien soll eine Liste erstellt werden mit dem Konzerttitel, dem Jahr, dem Ort sowie allen im jeweiligen Konzert aufgeführten individuellen Komponisten. Weiter sollen etwaige Konzertkritiken mit dem Namen des Kritikers und dem Text der Kritik dem jeweiligen Konzert beigeordnet werden. Die Konzertkritiken sollen alphabetisch nach dem Namen des Kritikers geordnet sein. Einige Elemente sollen im Laufe der Transformation umbenannt werden.

Die XQuery dazu lautet:

for $ko in //konzert
return <konzertkritik>{$ko/titel}<jahr>{substring($ko/datum, 1,4)}</jahr>{$ko/ort}
<komponisten>{string-join(distinct-values($ko//komponist),'/')}</komponisten>{
for $kr in doc('file:/C:/Users/CM/Documents/Tustep/beispiel/kritiken.xml')//kritik
where $ko/@id = $kr/@ref
order by $kr/../name
return
<kritik>{$kr/../name}<text>{data($kr)}</text></kritik>}
</konzertkritik>

In Tustep sodann:

#makro
$$ MODE TUSCRIPT
SET pfadsaxon  = "C:\Program Files\Oxygen XML Editor 15\lib\saxon9ee.jar"
SET pfadinput  = "C:\Users\CM\Documents\Tustep\beispiel\konzerte.xml"
SET pfadoutput = "C:\Users\CM\Documents\Tustep\beispiel\konzerteneu.xml"

EXECUTE "C:\Windows\System32\cmd.exe" *
DATA /C java -cp "{pfadsaxon}" net.sf.saxon.Query -s:{pfadinput}
DATA -qs:"for $ko in //konzert return <konzertkritik>{$ko/titel}<jahr>{substring($ko/datum, 1,4)}</jahr>{$ko/ort} <komponisten>{string-join(distinct-values($ko//komponist),'/')}</komponisten> {for $kr in doc('file:/C:/Users/CM/Documents/Tustep/beispiel/kritiken.xml')//kritik where $ko/@id = $kr/@ref order by $kr/../name return <kritik>{$kr/../name}<text>{data($kr)}</text></kritik>}</konzertkritik>"
DATA -o:{pfadoutput}
*eof

Das Resultat:

<?xml version="1.0" encoding="UTF-8"?>
<konzertkritik>
   <titel>10. Volkskonzert</titel>
   <jahr>1947</jahr>
   <ort>Berlin</ort>
   <komponisten>Musorgskij, Modest P./Cajkovskij, Pëtr I.</komponisten>
   <kritik>
      <name>Meier, Fritz</name>
      <text>War super.</text>
   </kritik>
   <kritik>
      <name>Müller, Max</name>
      <text>Nicht überzeugend.</text>
   </kritik>
</konzertkritik>
<konzertkritik>
   <titel>11. Volkskonzert</titel>
   <jahr>1947</jahr>
   <ort>Zürich</ort>
   <komponisten>Musorgskij, Modest P./Cajkovskij, Pëtr I.</komponisten>
   <kritik>
      <name>Müller, Max</name>
      <text>Hab schon Besseres gehört.</text>
   </kritik>
</konzertkritik>
<konzertkritik>
   <titel>4. Jugend-Konzert</titel>
   <jahr>1947</jahr>
   <ort>Heidelberg</ort>
   <komponisten>Mozart, Wolfgang Amadeus/Mendelssohn Bartholdy, Felix/Schubert, Franz</komponisten>
   <kritik>
      <name>Bäcker, Barbara</name>
      <text>Schön.</text>
   </kritik>
</konzertkritik>
<konzertkritik>
   <titel>Nachweihnachts-Konzert</titel>
   <jahr>1947</jahr>
   <ort>Zürich</ort>
   <komponisten>Labole, P. N./Sibelius, Jean/Scarlatti, Domenico/Borodin, Aleksandr P./Dvorák, Antonín/Bergson, Michael/Saint-Saëns, Camille/Eilenberg, Richard/Silva, Cayetano Alberto</komponisten>
   <kritik>
      <name>Bäcker, Barbara</name>
      <text>Wunderbar.</text>
   </kritik>
   <kritik>
      <name>Fischer, Moritz</name>
      <text>Enttäuschend.</text>
   </kritik>
</konzertkritik>
<konzertkritik>
   <titel>12. Volkskonzert</titel>
   <jahr>1948</jahr>
   <ort>Zürich</ort>
   <komponisten>Torelli, Giuseppe/Purcell, Henry/Stravinsky, Igor/Mozart, Wolfgang Amadeus/Haydn, Joseph</komponisten>
   <kritik>
      <name>Müller, Max</name>
      <text>Nicht schlecht.</text>
   </kritik>
</konzertkritik>
<konzertkritik>
   <titel>VI. Abonnementskonzert</titel>
   <jahr>1948</jahr>
   <ort>Basel</ort>
   <komponisten>Cherubini, Luigi/Brun, Fritz/Brahms, Johannes</komponisten>
   <kritik>
      <name>Fischer, Moritz</name>
      <text>Genial.</text>
   </kritik>
   <kritik>
      <name>Meier, Fritz</name>
      <text>Grottenschlecht.</text>
   </kritik>
</konzertkritik>



Zurück zur Übersichtsseite - TUSTEP und XML