- 1 Intro
- 2 Anwendungsgebiete
- 2.1 XSLT - die Programmiersprache im XML Bereich
- 2.2 Aktuelle und vergangene Anwendungen
- 2.3 Professionelle XML Verarbeitung
- 2.4 Technische Dokumentation
- 3 Wichtige Konzepte
- 3.1 Push vs. Pull Stylesheets
- 3.2 Eindeutigkeit der Regelbasis
- 3.3 Namespaces
- 3.4 Schemata
- 3.5 Standards
- 3.5.1 DITA
- 3.5.2 DITA Inhaltsmodell
- 3.5.1 DITA
- 4 Ausgewählte Themen
- 4.1 Transformationen mit XSLT
- 4.1.1 Vortransformationen
- 4.1.2 Komplexe XML-2-XML Transformationen
- 4.1.2.8 Vererbung
- 4.1.2.8 Vererbung
- 4.1.3 XSLT Streaming
- 4.1.3.1 XSLT Akkumulator
- 4.1.3.2 XSLT Iterator
- 4.1.4 Reguläre Ausdrücke
- 4.1.5 Modus vs. Tunnel Lösung
- 4.1.6 Identifikation mit
generate-id()
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.7 Webservice Calls mit doc() und unparsed-text()
- 4.1.8 Stylesheet-Parameter auf der Kommandozeile
- 4.1.9 Leerzeichenbehandlung
- 4.1.10 Mit
translate
Zeichen ersetzen
- 4.1.10.1 Spass mit dem Sequenzvergleich
- 4.1.11 Character Mappings in der Ausgabe
- 4.1.12 JSON mit XSLT 1.0 und Python lxml
- 4.1.1 Vortransformationen
- 4.2 Abfragen mit XQuery
- 4.2.5 XQuery als Programmiersprache
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.5.3.2 SQL Views in MarkLogic
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.6 Hilfreiche XQuery Schippsel
- 4.2.5 XQuery als Programmiersprache
- 4.3 XML Datenbanken
- 4.3.1 Connector zu Marklogic in Oxygen
- 4.3.2 Bi-Temporale Dokumente
- 4.3.2.1 Anlegen des Testszenarios auf der ML Konsole
- 4.3.2.2 Ausführen einiger Beispiel-Queries
- 4.3.3 Webapps mit MarkLogic
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.4 Dokument-Rechte in MarkLogic
- 4.3.5 MarkLogic Tools
- 4.3.5.1 EXPath Konsole
- 4.3.5.2 mlcp - MarkLogic Content Pump
- 4.3.5.3 Deployment-Tools
- 4.4 XSL-FO mit XSLT1.x
- 4.5 Testing
- 4.5.1 Validierung mit Schematron
- 4.5.2 Erste Schritte mit XSpec
- 4.5.1 Validierung mit Schematron
- 4.6 Performanz-Optimierung
- 4.1 Transformationen mit XSLT
- 5 Zusätzliches Know-How
- 5.1 XML Editoren
- 5.2 Quellcode-Versionskontrolle
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.2.2 GIT Kommandos
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.1 XML Editoren
- 6 Glossary
- 7 Tektur CCMS
4.1.6.4.1 Funktionen und Module
4.1.6.4.1.1 Funktionen
Um bestimmte Abschnitte des XQuery Programm wiederverwendbar zu machen, stehen Funktionsdeklarationen zur Verfügung. Eine einfache Funktion wäre z.B. diese hier:
declare function local:wrap-header($json) { xdmp:add-response-header("Pragma", "no-cache"), xdmp:add-response-header("Cache-Control", "no-cache"), xdmp:add-response-header("Expires", "0"), xdmp:set-response-content-type('text/json; charset=utf-8'), $json };
Sie wickelt um einen JSON String eine passende Header Information.
Damit die Funktion eingebunden werden kann, muss ein passender Namespace deklariert werden:
declare namespace local = 'local:';
Nicht nur bzgl. Wiederverwendbarkeit sind Funktionen praktisch, sondern auch
um ganz elementare Konstrukte, wie
while...do
Schleifen, zu realisieren.
Dazu nutzt man - wie in der funktionalen Programmierung üblich - die Rekursion:
declare function local:ist-letzter-wert-in-kette($glied) { let $wert := local:komplizierte-berechnung($glied), $naechstes-glied := local:komplizierte-berechnung-der-position($glied), return if ($naechstes-glied and not($wert = 'foobar')) then local:durchlaufe-kette($naechstes-glied) else $wert = 'foobar' };
In diesem kleinen Schnippsel sind schon einige Besonderheiten von
XQuery zu sehen. Variablenzuweisungen geschehen mit einem Doppelpunkt,
Vergleiche dagegen nur mit einem einfachen "=". Statements werden mit einem
Komma getrennt.
4.1.6.4.1.2 Funktionsaufrufe im XPath
Wenn eine Funktion auf einer Kontenmenge operiert, dann kann der Funktionsaufruf auch an einen Pfadselektor gehangen werden, bspw. so:
<xsl:value-of select="sum($current/betrag[xs:decimal(.) gt 0]/xs:decimal(.))"/>
Hier werden die
betrag
-Knoten eines zuvor selektierten Teilbaums, der in der Variablen
$current
abgespeichert ist, aufsummiert - aber nur wenn der Wert größer als 0 ist.
Der Funktionsaufruf ist hier ein Type-Cast auf einen Dezimalwert, um eine gewisse Rechengenauigkeit zu gewährleisten.
Die Filterung auf positive Werte ist dabei noch gewöhnlich formuliert:
[xs:decimal(.) gt 0]
xs:decimal
nimmt den aktuell ausgwählten Knoten und macht einen Dezimalwert daraus, um ihn mit 0 zu vergleichen.
Falls hier an den Typkonstruktor
xs:decimal
ein nicht-unterstütztes Format übergeben wird, bspw. ein String, dann wird ein fatalen Fehler geworfen und das Programm bricht ab.
Der Funktionsaufruf kann aber auch als Pfadselektion an einem XPath angebracht werden:
/xs:decimal(.)
Im Fehlerfall wird der fehlerhafte Wert nicht summiert und das Programm läuft weiter.
$current/betrag[xs:decimal(.) gt 0]
Auf herkömmlichen Weg würde man eine Schleife verwenden, die alle Werte auf deren Dezimalwert abbildet:
sum(for $x in $current/betrag[xs:decimal(.) gt 0] return xs:decimal($x))
Das ist ein bisschen komplizerter, gewährleistet aber eine bessere Robustheit der Programmierung.
Funktionsaufrufe als Pfadselektoren brechen bei einem Fehler in der Funktion - ohne explizite Ausnahmebehandlung - nicht ab.
Falls mehr Robustheit gefordert ist, sollte man über Ergbnisknotenmengen iterieren und Funktionsaufrufe auf herkömmlichem Weg absetzen.
Betrachten wir folgendes XQuery-Schnippsel:
collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)]/xdmp:node-collections(.) [starts-with(., '/buchung')]/xdmp:collection-delete(.)
Hier sind alle Abrechnungen in einer Collection
/abrechnung
gespeichert. Die Abrechungen mit den Vorgangsnummern
3,8,9
und
10
sollen
herausgefischt werden. Diese Abrechungen können auch in verschiedenen Collections verwaltet werden, bspw. mittels eines Collection-Typs
"Buchung". Eine Buchung-Collection sammelt alle Abrechungen, die an einem bestimmten Buchungstag getätigt wurden. Wir gehen jetzt davon aus,
dass alle Abrechungen
3,8,9,10
an einem bestimmten Buchungstag getätigt wurden - und nur diese. Aus irgendeinem Grund wollen wir diese
Buchung nun löschen. Das macht genau der obige Einzeiler. Der Filter:
collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)]
gibt eine Knotenmenge zurück. Das gefilterte Funktionsergebnis
xdmp:node-collections(.)[starts-with(., '/buchung')]
ist auch eine Knotenmenge. Normalerweise bräuchten wir also Schleifen, um über diese Mengen zu iterieren. Das würde irgendwie so aussehen
let $filtered-collection := collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)], $collections-to-be-deleted := distinct-values( for $x in $collection return ( for $y in xdmp:document-get-collections(fn:document-uri($x)) [starts-with(., '/buchung')] return $y ) ) return ( for $culprit in $collections-to-be-deleted return xdmp:collection-delete($culprit) )
Der Quelltext ist zwar so wesentlich länger, aber auch weniger geübte XPath-Experten erkennen leicht, um was es geht.
4.1.6.4.1.3 Module
Um eine XQuery Anwendung zu modularisieren, können einzelne Skripte in
Module ausgelagert werden. Ein Modul, z.B.
common.xqy
, wird dabei über einen eigenen
Namespace deklariert:
module namespace common = "https://www.tekturcms.de/common";
Dieses Modul kann dann in anderen Skripten eingebunden werden:
import module namespace common = "https://www.tekturcms.de/common" at "common.xqy";
Funktionen und Variablen werden dann mir dem Namespace geprefixt aufgerufen:
Funktionsaufruf: common:wrap-response-header(...) Variablenauswertung: $common:collection-books